We keep two implementations of the same trading logic. Research work happens in Python, where it is fast to try things. Live trading and backtesting run on a separate engine, because that is what has to be correct at nine in the morning.
Two implementations of the same rules will drift. The question is how you find out before your money does.
1. Compare intermediates, not outcomes
The obvious check is to run both and compare the trade list. It is also the weakest check available, because a trade list is the end of a long chain. Two errors can cancel. A difference in an indicator can leave the trades identical on one day and change them on the next.
So the comparison runs at every stage of the chain: preprocessing, candle patterns, microstructure, volatility, volume profile, stop and take levels, risk scoring, the strategy DSL, the order manager, and finally the backtest. Each stage is compared value by value against fixed inputs.
| Stage group | Cases |
|---|
| Preprocessing and raw candles | 34 |
| Pattern and microstructure inference | 83 |
| Model training paths | 30 |
| Risk analyzer, per symbol | 40 |
| DSL, order manager, backtest | 9 |
Every case is pinned to a real recorded session and ticker, so the inputs are market data we actually saw rather than synthetic sequences.
2. Result
213 cases, 3,373,795 compared values, zero mismatches. The largest single case compares 170,000 preprocessed rows.
5,054 values landed inside a declared boundary tolerance and are counted separately rather than being silently accepted. The rule is that ties are reported and the run fails if their share crosses a threshold, so a slow drift into tolerance cannot hide as a pass.
3. Floating point is not a rounding detail
The most useful thing this harness caught had nothing to do with logic.
NumPy's mean and sum use pairwise summation; a straightforward sequential loop does not. Over a long window the two disagree in the last bits. That is invisible until a value sits on a decision boundary — and a risk score that crosses a bucket edge changes a trade.
On one fixture, a sequential sum produced 9 differing risk scores out of 6,000 rows. Matching the summation strategy brought that to zero across 8,118 checks. The logic had been identical the whole time.
Since then the comparison policy states its tolerances explicitly rather than leaving them to chance: exact equality for integers and categories, relative tolerance for ratios, a looser bound for compound products, and an absolute floor for values near zero where relative error is meaningless.
4. What it does not prove
A parity harness proves that two implementations agree. It says nothing about whether they are right. Both can share a wrong assumption and pass every case.
That is why this sits alongside the other checks rather than replacing them: eligibility audits for whether the inputs could have existed, cost re-pricing for whether the result survives contact with a spread, and paper trading before anything is funded. Parity is the cheapest of the four and catches a category the others cannot — silent divergence between the thing you researched and the thing that trades.
Conclusion
If you maintain a research implementation and a production one, compare them at every stage on real recorded inputs, declare your tolerances instead of discovering them, and treat summation order as part of the specification. The bug we found this way was not in anyone's logic.