What a Swapped Field Does to a Backtest
On July 30, 2026 we replaced every backtest figure published on this site. Not adjusted — replaced. The cause was a data-decoding bug in our own pipeline, not a strategy failure, a market-regime shift, or anything happening in a live account. This is the record of what the bug was, how we found it, what we did about it, and why the corrected numbers are worse than the ones we had shown before.
If you only want the current results, they live on the performance page. This post is about the mistake behind the ones that came before it.
The Defect
Our backtest engine reads Dukascopy hourly bar files directly rather than going through a broker’s strategy tester. Each bar in that file carries, among other fields, two extreme prices for the hour: a high and a low. Our decoder read those two fields in the wrong order — on every single bar, without exception, the value we treated as the high was actually the low, and vice versa.
That is a narrow, mechanical bug, but its blast radius was not narrow at all. Once the high and low are swapped, everything computed from the range of a bar is wrong: stop-loss and take-profit touch detection (did price reach the exit level within the hour?), Average True Range, and the directional components of ADX. Those three things underpin almost every piece of trade-management logic our EAs use — where a stop sits, whether a target got hit before a stop, whether a trend filter agreed with a trade. A bug that swaps two numbers in a data feed does not sound like it should be able to flip a strategy’s published verdict from “passes” to “fails,” but that is exactly the kind of error that can, because the corruption is systemic rather than occasional. Every number we had published from this pipeline had been produced on price data that could not have existed — bars where the low was above the high.
How We Found It
The defect surfaced during review work on the pipeline, not from a user report or a live-account discrepancy — we do not have a live track record to compare against, which is itself part of why we lean on the honesty of the testing process rather than a track record we don’t have. Once the swapped fields were suspected, verification was straightforward: for a swapped-field bug, checking whether the decoded high was ever below the decoded low across a sample window either confirms or rules it out immediately, and it confirmed it. There was no ambiguity to resolve or judgment call to make once we looked — the data was internally inconsistent in a way that a correct decoder cannot produce.
What We Did
The fix itself is a small code change: correct the field order in the decoder so a bar’s high is never below its low. The larger question was what to do with everything downstream of the old, wrong data, and we treated that as the actual scope of the fix rather than an afterthought:
- Fixed the decoder. No shortcuts, no patching results after the fact.
- Discarded the cached data. We did not try to selectively repair the cache in place — every cached bar file that had been produced by the broken decoder was deleted rather than trusted.
- Re-downloaded everything from Dukascopy. Fresh data through the corrected decoder, for the full test window.
- Re-ran every published test with no changes to any EA’s parameters. This is the part we want to be explicit about: nothing about SnapBack, QuickPulse, or DualHorizon’s entry logic, stop distance, target distance, or risk settings changed between the old run and the new one. The only variable that moved was whether the price data going in was correct.
That last point matters because the honest version of a correction like this resists the temptation to also “improve” things while you’re in there. If we had adjusted a parameter at the same time as fixing the decoder, we would no longer be able to say the resulting numbers isolate the effect of the bug — and we would have handed ourselves an excuse to explain away a bad number as a tuning choice rather than a corrected measurement. We wanted one clean before/after comparison, so we made exactly one change.
What Changed
The full corrected tables are on the performance page; here is the shape of what moved.
QuickPulse failed on every pair and is retired. Before the correction, QuickPulse showed a profit factor of 1.44 on EURUSD, 1.35 on USDJPY, and 1.26 on GBPUSD across roughly 320–370 trades per pair. On corrected data, all three pairs land at 0.93–1.00 profit factor across 521–556 trades — flat to slightly negative, and none of it clears our 1.3 profit-factor gate. The strategy’s math (a tight 1.3× ATR stop against a 6.0× ATR target, needing roughly an 18% win rate to break even) is internally coherent, and the corrected win rate lands at 17.5–19.0% — right on that break-even line rather than above it. We are not recommending QuickPulse for live use, and its EA page is now framed as a post-mortem rather than a pitch. It remains downloadable for study.
DualHorizon narrowed from three candidate pairs to one. It now passes our quality gate on EURUSD only (profit factor 1.41, 126 trades). USDJPY, which we had previously recommended, dropped to a profit factor of 1.11 on corrected data — still marginally profitable over the window, but below our 1.3 threshold, so it is no longer recommended. GBPUSD was negative both before and after.
SnapBack survived, with one pair weaker than shown before. SnapBack still clears the gate on EURUSD (profit factor 1.65, 79 trades) and GBPUSD (profit factor 1.57, 78 trades). GBPUSD’s corrected figure is meaningfully lower than what we had shown previously, though it still passes. USDJPY did not pass either before or after the correction (corrected profit factor 0.58).
We also corrected an unrelated error on the same page: it had named the wrong testing platform (MetaTrader’s Strategy Tester instead of the Python pipeline we actually use). Both corrections are recorded in the page’s Correction History section rather than folded into a silent edit, and any future correction will be appended there the same way.
Why We Published the Worse Numbers
There was no requirement forcing us to publish any of this. No regulator was asking, no user had flagged a discrepancy, and quietly re-running the numbers and updating the tables without comment was an option nobody would have caught. We didn’t do that, for a reason that has nothing to do with the specific numbers involved: a testing pipeline you only trust when it produces good news is not a testing pipeline you can trust at all. The value of publishing backtest figures at all depends entirely on the process behind them being one we would show you honestly whether the answer was flattering or not. Retiring QuickPulse’s recommendation the same week it stopped clearing the gate, rather than letting it sit quietly on the download page with stale numbers, is the same principle applied at the level of a single EA.
This also is not a story about a strategy that used to work and stopped working in live markets — none of these EAs has a live track record to begin with, corrected or otherwise. It is a story about a measurement being wrong, and about what the measurement now shows once it’s right. Worth remembering regardless of what any backtest says: most retail traders who trade forex lose money, and a passing backtest — corrected or not — describes a simulation on historical data, not a promise about your account. Our editorial policy covers how we handle corrections generally; this is the largest one we’ve had to make.
