Backtest Forensics/ Fixed scope, fixed price

Know the number is real before the money goes in.

We check trading strategies and yield claims, so the number you vote on, invest on or raise on is one that holds up. That is the whole job.

Fixed scope, fixed price · $2,500 – $25,000 · report in 1–6 weeks · no retainer, no lock-in

dvt@arcobaleno.cloud → Start with a free 30-minute call. If what you have isn't enough to reproduce anything, we'll say so on that call and turn the work down.

What it costs

Three fixed prices. Start small with a Triage; every finding comes with a script you can run yourself.

Triage
$2,500
Find out in about a week whether the headline number is even real.
One strategy, one number. Can we get your headline result out of the code and data you send us? You get a 3–5 page memo: it reproduces, it doesn't, or it does with caveats — and exactly why.
2–3 days of work · report in about 1 week
Full audit
$12,000
Go into the vote or the investment committee with every number checked.
One strategy, start to finish: where the data came from, how the asset list was built, what trading was assumed to cost, how the version was chosen, and whether the claim holds up. A 15–25 page report, every finding with a script that reproduces it.
5–8 days of work · report in 2–3 weeks
Portfolio review
$25,000
Know which of your strategies hold up, and which share the same hidden mistake.
Three to six strategies, plus the shared code they all run on. A report for each, and a section on what they have in common — shared bugs are the normal case, not the exception.
12–15 days of work · report in 4–6 weeks

The Triage fee is credited in full against a Full audit of the same strategy booked within 30 days. Report times run from the day we have everything we asked for, and are typical rather than guaranteed.

Sample report See exactly what you would get: a complete full audit of a made-up strategy, all eight checks, every finding explained, and the scripts that reproduce every one of them. What we need from you The code, the data, and the claim written down. Formats, sizes and the markets we cover.

What you walk away with

A report you can put in front of your members, your investment committee or your investors, with the reasons behind every number. Whichever way it comes out, you have something to show: “an independent check found the advertised 38% was really 2%, and here is exactly why”, or “an independent check rebuilt the number from the code and found nothing wrong.” Both are worth having before a vote, not after.

How it works, from first call to report

It starts with a free 30-minute call. If you don't have what's needed to reproduce the result, we'll say so on that call and turn the work down — an audit that can't reproduce anything wastes your money and our time. Otherwise: you send the code, the data or access to it, and the result you're claiming. We quote one of the three prices above. You get the report, plus a follow-up call to walk you through it.

What we need from you, and what suits us best — formats, sizes and the markets we do and don't cover.

Sharing the report: what's allowed

Share it in full: it says what was checked, what was found, and what wasn't checked, so nobody reads more into it than it says. It is not a rating, a seal of approval or a forecast, and the strategy can't be marketed as “verified by Arcobaleno” without our written agreement. Anyone outside your organisation who wants to rely on it, to invest for example, signs a short letter with us first.

Why this costs more than an automated check

Automated strategy validators re-run your rules, as you describe them, on their own clean data. That can tell you whether the idea holds up. It can't find a mistake in the code and data you actually used — how your asset list was built, where your cost figure came from, how your code fills orders, what happened to positions that never closed. Most of the ten findings below sit exactly there. We work on your material, not a copy of your idea.

Ten findings, from our own research

Every one is a mistake we found in our own research and published against ourselves, because we can't show you a client's.

01 Repeatability

The result changed every time it ran

Reported as −1.3191%. Really: a different number on every run. Read the finding ›Close ×
The reported result
−1.3191%
Average return per trading period, published as the strategy's performance.
→
What it really was
A different number every run
Same code, same data, no answer that came back twice.

The code ranked assets and bought the best ones. When two assets tied, something had to break the tie — and the program broke ties differently each time it was started. Different assets went into the portfolio on different runs.

Ties weren't some rare edge case. On this exchange, traders pay each other a small fee every few hours (funding), and 55% of those payments sit at exactly the same minimum value, so any two assets resting on that minimum looked identical to the ranking code.

Same code, same dataResult it produced
Run 1−1.3679%
Run 2−1.2256%
Run 3−1.2569%

In the periods where the runs disagreed, the typical gap was 1.57 percentage points. That isn't rounding. That's a different portfolio.

Why it matters Nobody could have reproduced this number — including us, running our own code on our own machine. Every decision that followed rested on a figure that would not come back. The fix was one line.
From our own records: research/runs/r17-corrected-holdout/AMENDMENT_001.md
02 Bad data

One bad number produced a 192% annual return

One day's figure: 172,635%. The pool normally earned 0.91%. Read the finding ›Close ×
What the data provider sent
172,635%
One day's yearly fee rate, for one investment pool.
→
What that pool normally earned
0.91%
The same provider's figure on a typical day.

That single day fed into thirty separate monthly averages, and produced a result of +192.66% a year. The pool held two of the largest and most closely linked crypto assets, where a return like that isn't merely unlikely — it can't happen.

Every safety check passed. The checks all looked at the averages, and the bad number got in before the averages were built. On screen it looked like an exciting outlier, not like broken data.

The research already contained the right test. It had been run — on two pools, on one day. That's a spot check. Run across everything, it catches the bad day immediately.

Why it matters A spot check and a real safeguard look the same when you read the code. The difference is whether it runs on every row. A number any human would catch in a second can be completely invisible to software that was never asked to look.
From our own records: research/runs/amm-lp-v1/
03 Cost arithmetic

Three strategies closed over a cost that was 20x too high

Costs charged: ≈2.0% a period. Actually incurred: 0.10%. Read the finding ›Close ×
Costs the code charged
≈2.0%
Per period. Added up across positions, while profit was averaged across them.
→
Costs actually incurred
0.10%
Per period. Overcharged by roughly twenty times.

The effect was backwards on its face: spreading the same pot of money across more assets made it look more expensive to trade. It can't. Splitting a fixed amount across more positions makes each position smaller. We checked the arithmetic across all 590 periods of three sets of records.

Corrected, the strategies looked transformed. On the standard risk-adjusted score — where below zero means it lost money — one went from −8.73 to +0.34, another from −2.83 to +1.39.

All three stayed shut. Those flattering new numbers came from the very data the strategies were built on, and the margin of error was wide enough to include zero — so the improvement might be nothing at all. The only honest test was data the strategy had never seen. Against that, the best of the three failed outright, losing money both before and after the stretch it was built on. It had been tuned too closely to its own past, not worn out.

Why it matters A wrong cost assumption is dangerous in both directions — it buries strategies that work and flatters ones that don't. Finding the bug is half the job. Refusing to let it raise the dead is the other half, and it's the half someone with a stake in the answer will skip.
From our own records: research/runs/cost-floor-v1/ · research/runs/r17-corrected-holdout/VERDICT.md
› Seven more findings — stale data, inconsistent costs, a range read as a headline, versions that might make nothing, thin evidence, losses left off the books, and a fix for missing coins that fixed nothing
04 Stale data

A filter for recent data that never read a date

The “recent data” filter counted rows. It never read a date. Read the finding ›Close ×
The safeguard, as written
Count the rows
Named and trusted as the check that kept out stale data.
→
What it actually checked
Nothing about time
It never read a date. Frozen data stayed eligible indefinitely.

A data migration quietly froze 65 files — they simply stopped updating. Because the filter counted rows instead of reading dates, those frozen assets stayed eligible for about 26 days. The strategy's holdings changed completely from one week to the next, which read like the strategy responding to the market. It was the data breaking underneath it.

Then a second freeze was found inside the restarted clock: three more assets stopped updating on the same day, and one was bought into a portfolio a full week after it had already gone dead.

Underneath that sat the worst of it. One position the strategy held had not traded at all — zero out of 1,441 minutes with a single trade in them. A full-size position, booked at a profit, describing a trade nobody on earth could have made.

Why it matters A filter named after a job does not necessarily do that job. And when a strategy suddenly starts behaving differently, the first thing to suspect is the data — not the market.
From our own records: research/runs/weekend-drift-selection-audit/
05 What trading really costs

One cost, three documents, three different numbers

The screen price said 0.26%. A real order cost 0.48%. Read the finding ›Close ×
Measured at the screen price
0.26%
Cheaper than the strategy assumed. Looks reassuring.
→
Measured by filling a real order
0.48%
Dearer than it assumed. The two don't just differ in size — they differ in direction.

The price on your screen is the price for a tiny trade. Put a real order through and you eat into worse and worse prices as you go. On this basket the screen price suggested a cost of 0.004%–0.033%, while getting in and back out actually cost 0.28%–0.78%. A measurement taken at the screen price cannot see that, at all.

So we went back and audited every cost assumption in the research. It found this:

A standing study had assumed the worst possible round trip on that exchange was 0.31%. Filling real orders, 40 of 122 assets cost more than that at $10,000 apiece — the worst of them 0.92%. And those were the easiest-to-trade assets available.

That study's own follow-up note had already tried to fix it with a textbook formula — and overshot by about 1.8× the other way, exactly as the note itself suspected it might. Measured properly, the truth sat between the two. The original conclusion survived at the size it was written for, and failed at ten times that size.

Why it matters Trading cost is something to measure, not something to inherit. And it is worst precisely where a ranking rule tends to pick, because ranking rules choose for returns — not for how easily something can be bought and sold.
From our own records: research/runs/cost-model-audit-v1/VERDICT.md
06 A hidden range

A headline that was really a range

Quoted as one exact number. Really a 4.2-point range. Read the finding ›Close ×
Published, and repeatable
One number
Same answer on every run. Quoted to two decimal places.
→
What it actually was
A 4.2-point range
The published figure sat near the top: only 4 in 100 runs came out higher.

The strategy trades when certain market events happen, and can hold only so many positions at once. On the worst days far more signals arrive at once than there are slots — the busiest single hour had 194. Something has to choose which ones get in, and that choice was being made by the arbitrary order the sorting step happened to leave the data in. 72% of all events shared their hour with at least one other.

Two of our own programs, same data, same settings, produced annual returns 1.4 percentage points apart. Both were running correctly. So we re-ran it 300 times, shuffling only within each tied hour: the answer typically moved by 0.76 points, and the best ordering beat the worst by 4.2.

Worse than the number itself: a cross-check had already been run and had passed. One study cited the other's agreement as proof its simulator reproduced the original. The two figures were 1.4 points apart, and the gap was hidden inside a rounding in the summary. A verification step read as independent confirmation when it was two programs sharing one defect.

Why it matters A figure that comes back identical every time still is not necessarily reproducible — it can be one fixed draw from a range nobody has measured. And what survived matters as much: the risk-adjusted return, the max drawdown and the conclusion about position size were all unchanged. The headline was overstated by 1.2 points; the reasoning underneath it was sound. An audit that only reports damage is not worth buying.
From our own records: research/runs/cascade-selection-v1/asof-rerun-2026-08-26/
07 Maybe nothing

Nine versions, and none could be told apart from zero

Measured at 42.8% a year. Honest range: −9.0% to +126.4%. Read the finding ›Close ×
The rule, as first measured
42.8% a year
A convincing-looking edge across nine versions.
→
Its honest range
−9.0% to +126.4%
Includes zero. As did all nine.

The usual way to measure how sure you can be is to shuffle the daily results thousands of times and see how widely the outcomes spread. That assumes each day is independent of the last. This rule held the same position for days at a time, so consecutive days moved together — and treating them as independent counts the same evidence over and over, making the range far too narrow.

Reshuffling in blocks of ten days instead, which keeps that clumping intact, every one of the nine versions had a range that included zero. Splitting the history in half pointed the same way: performance fell in every version, one from a risk-adjusted score of 0.88 to 0.09.

It is worth saying what the problem was not. Trading costs barely mattered: the score went 0.98 before fees, 0.95 at normal fees, 0.85 at four times normal fees. This failed on uncertainty, not on costs — which is exactly the wrong place to have been looking.

Why it matters Nothing was wrong with the returns. What was wrong was the confidence attached to them — and confidence is what someone actually acts on. We stopped pursuing it, which is a result, and getting it in an afternoon is cheaper than getting it from the market.
From our own records: research/runs/deribit-iv-screen-v1/VERDICT.md
08 How much evidence there really is

Two thousand trades, six weeks of evidence

2,050 trades, but only 6 weeks of independent evidence. Read the finding ›Close ×
The track record, as described
2,050 trades
238 assets, six and a half years.
→
Independent observations
6 weeks
Behind the best-looking result in the study.

The strategy only fires during rare market-wide episodes, so its trades arrive in a small number of clusters — and every trade inside a cluster is the same event happening to different assets. Counting distinct weeks instead of trades, the strongest result in the study rests on six weeks in six and a half years. About one independent observation a year.

The trap that sets is not theoretical. The obvious improvement — sit out the small isolated events, save room for the broad ones — tested better on data held back for the purpose (0.88 → 1.46) and worse on the data it was built with (1.16 → 0.70). A rule that improves on unseen data is the opposite of the usual warning sign and reads like a discovery. Its entire advantage rested on two crash weeks. We did not adopt it.

Why it matters Trade count is not evidence count, and the gap between them is where false confidence lives. A strategy can be genuinely profitable and still have almost no evidence behind it — those are different questions, and a big number of trades only answers one of them.
From our own records: research/runs/cascade-selection-v1/VERDICT.md
09 Positions left open

Profitable only if you ignore what it still holds

8 of 8 versions profitable on closed trades. 0 of 8 counting what was still held. Read the finding ›Close ×
Counting completed trades only
8 of 8 profitable
Every configuration tested made money.
→
Also valuing what it still held
0 of 8 profitable
The same eight. The sign flips in all of them.

The strategy places a ladder of buy orders below the price and sells above. Each completed round trip books a small gain — but when the price falls and keeps falling, the buys fill and the matching sells never do, so the position sits there losing. At the end of a test period, what do you do with the purchases still open? Value them at the current price, or leave them out because they never closed?

We ran all eight versions both ways across 20,032 test windows. The accounting choice flips the sign in every configuration. The gap tracks exactly what you would expect: the versions holding the most unsold purchases show the widest gap, one going from +2.4% counting only closed trades to −5.1% counting everything.

One note on how we treated our own claim. The original study made this point in a single line and stated the other figure without ever calculating it — the code tracked both figures and saved only their sum. We measured it before publishing this, and the assertion turned out to be conservative: it said “most versions”, the answer is all of them. Being right by luck is not the same as having checked.

Why it matters The mechanism books small wins and holds its losses, so discarding what is still open deletes the losses and keeps the wins. The result is not wrong by a little — it is the opposite sign. And nothing looks broken in the output, because nothing went wrong computationally. We simply chose, probably without noticing it was a choice.
From our own records: research/runs/grid-trading-v1/fc7-realized-2026-09-11/
10 Missing losers

The fix for missing coins that brought back none of the dead ones

Said to be fixed. It brought back 0 of 31 disappeared coins. Read the finding ›Close ×
What the repair was called
“Fixed”
The repaired price archive, said to include coins that had since disappeared.
→
Disappeared coins it actually brought back
0 of 31
All 63 coins it did add back were still on the exchange, about to close.

Our own price archive had a known gap: it only held coins that were still listed on the day we downloaded it, so every coin that had already closed was missing. Those are usually the losers. We built a repair, 63 coins came back, and from then on the archive went by a name that said the problem was solved.

It wasn't. The repair had asked the exchange for coins that were about to close, and a coin that has fully closed is no longer on that list at all. So the repair could never have found a single one. It looked complete because of its name, not because anyone had checked it. The exchange's own public archive had every closed coin all along, one download away.

Then we measured whether it changed the answer, and it didn't. Closed coins turned out not to be short-lived oddities: they lived 22 months on average, against 21 for coins still trading, and closed at all points between 2020 and 2026. On the result in question, the missing coins accounted for about 1% of the effect, a rounding error. We had written down what we expected to find before measuring it, and that guess turned out to be wrong.

Why it matters A known problem is one you are still watching. A “fixed” problem is one you have stopped looking for, and the name on the fix answers the question for anyone who asks later. That is why this check has two halves: was the list of coins built as it looked at the time, and was any earlier fix tested on what it claims to fix. Here the honest answer was that the gap was real and changed nothing. An audit that only reports damage is not worth buying.
From our own records: research/runs/premium-panel-hist-v1/VERDICT.md
Most backtests aren't wrong. They're just unchecked.

A backtest is a trading strategy run against past market data to see what it would have made. That one number decides everything that follows — whether to fund it, how much to risk on it, whether to walk away.

The trouble is that people check the things they thought to check. Mistakes survive in the places nobody thought to look: a filter that doesn't do what its name says, a fee copied from somewhere else instead of measured, a list that sorts itself differently every time the program runs.

Not one of the problems below needed new data or a cleverer model. Every one was sitting in our own code, and we could have found any of them on a day we thought to look. That is rather the point: this is what checking finds.

And if you already know your strategy works, you probably also know the one time it didn't. Most people can name that trade. It is usually explained as bad luck, and sometimes that is exactly what it was. But the question worth answering is not whether the strategy works — it is whether that miss was one event or a class of them, and how much of the rest of the record rests on the same assumption that failed. The backtest cannot tell you: it is the thing that scored the event as a win.

What these ten have in common
The eight checks every audit runs
  1. Does it repeat? Run the code twice, on two different machines. Does the same number come back?
  2. Are the dead ones missing? Was the list of assets built as it looked back then, or from the ones still around today? The ones that disappeared are usually the losers.
  3. Are the costs real? Where did the fee figure come from? Is it charged correctly? Was it measured on realistic order sizes, or taken off the screen?
  4. Is the data clean? Was every row checked, or just a sample? Can one bad day poison a long average?
  5. Is the data current? Do the filters meant to exclude stale data actually look at dates?
  6. Was it cherry-picked? Was this version chosen before or after seeing how it performed? Is there genuinely untouched data left to test against?
  7. Could you have traded it? Could every position in the result actually have been bought and sold, at the price used?
  8. What is really being claimed? A single number, or a range? Is the margin of error wide enough to include zero — meaning it might be nothing?

Each check in detail, what we need from you, and the markets we cover →

Who this is for

Whoever loses money if the number is wrong. A DAO or treasury committee being shown a yield product. An investor checking a fund manager's track record. A partner about to fund a strategy built in-house. A founder raising money on a performance claim, before someone else checks it for them.

Backtest forensics · Oslo
dvt@arcobaleno.cloud →

Start with a free 30-minute call. Tell us what the strategy is and what you have — code, data, and the result being claimed. If that isn't enough to reproduce anything, we'll tell you on the call and turn the work down.

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. Every case study on this page describes a defect found in our own internal research; no client work is described. Figures are quoted as they appear in the dated internal documents named beneath each case.

Arcobaleno Research is a trading name of ARCOBALENO RESEARCH VO, a Norwegian sole proprietorship (enkeltpersonforetak), org. nr. 918 838 120, registered in Oslo, Norway. Run by its proprietor — profile on LinkedIn.