Research says the book should have made one number. The account made another. Most post-mortems stop at "live was a bit worse, mostly slippage and fees."
That sentence can't be checked. How much was slippage? How much was fees? Was part of it not a cost at all, but orders that never filled? If you can't split it, you can't tell whether to fix execution, fix the cost model, or go back and question the signal.
After every close I now split the day's difference between "what research says the book earned" and "what the account earned" into four parts. The four parts add up to the whole gap, with nothing left over. The report prints a closure residual on its own line, and it has to be zero. If it isn't, my accounting is wrong, and that gets fixed before any conclusion is drawn.
Below is a synthetic worked example. The numbers are made up and are not anyone's performance; they only show how the split works.
Define both lines first
Before splitting the gap, "research P&L" and "live P&L" need the same window and the same target positions. Otherwise the gap mixes in a difference of definitions.
Research line R: take the target shares actually sent that day, value them from the decision-time price to the same time on the next session, and subtract the modeled costs (slippage, commission, borrow).
This is a deliberate choice. R is not a separate backtest. It is the live targets, priced at research times, with the research cost model. The gap between the two lines then contains execution only, not "research and live picked different stocks."
Live line LW: the broker-reported holdings after the fills, same window. Shares traded that day enter at their fill price; subtract the actual commission and the actual borrow charge.
The gap equals four parts, exactly
This is not an approximation. Substitute the definitions of R and LW, expand, and the terms cancel down to these four. That makes it a check: if the four parts don't add up to the gap, the data or the code is wrong.
Each part answers a different question:
| Part | What it measures | Where to look |
|---|---|---|
| Unfilled positions | P&L on shares you meant to hold but didn't | Rejections, partial fills, untradeable names, extra non-target holdings |
| Execution | Fill price vs decision price, against modeled slippage | Order timing, order type, liquidity |
| Commission | What the broker charged vs the model | Per-order minimums, fee tiers, split orders |
| Borrow | Actual short borrow cost vs the model | Hard-to-borrow names, rate changes, when charges are booked |
Worked example: live "won", and that's not good news
The synthetic book has five stocks and one hedge ETF: one partial fill, one rejected order, one short, and one name whose next-session price is missing. Working through the definitions above:
The gap is negative: live lost 105 dollars less than research. Looking only at the total, it is easy to conclude execution went well.
The split shows something else. 90 of the 105 dollars come from unfilled positions: one stock only partly filled, and the shares that didn't fill went down that day. That is luck, not execution skill. On a day the stock rises, the same missed fill becomes a loss.
The 3.60 under execution needs care too. Modeled slippage is charged on the full target trade, but unfilled shares have no fill price, so on a day with incomplete fills, execution looks better than it was. Read execution together with unfilled positions.
Borrow shows a different effect: the model accrues the annual rate every day, but the broker hasn't booked a borrow charge yet, so the actual is zero. Like commission, part of this line can be booking timing, so judge it over several days.
A fifth number: the pricing-time difference is not a loss
The research line prices at decision time; account statements usually use the close. Same positions, different pricing time, different P&L. In the example that difference is 123.50 dollars, larger than all four parts combined.
This isn't a live loss; the two sides price at different times. It accumulates day by day, but over a longer run the two conventions differ only at the start, at the end, and on rebalance days between decision time and the close. So it gets its own line and never mixes with the four parts. Otherwise a late-afternoon move gets read as execution getting worse.
Missing data: list it, don't treat it as zero
One stock in the example has no next-session price. The calculation leaves it out of every line and names it in the report. Quietly setting its return to zero would remove the same piece from both lines, the residual would still be zero, nothing would raise an error, and the missing data would be hidden.
A whole day that can't be reconciled gets the same treatment: marked missing with a reason, kept out of the cumulative figures, and later days reconcile as usual. In an early version of mine, one gap made every later day fail to run.
What running it taught me
I ran this reconciliation first on a broker's paper-trading account. The four-way split caught several things the total would have hidden:
- Unfilled positions caught an incident. One day, holdings didn't match targets on twenty-odd names. The deploy log showed a protective switch had tripped during a strategy version change and the missing positions were never re-ordered; it was fixed the next day. Looking only at P&L, that day was just "a bit worse."
- The per-order minimum commission was close to three times the model's. The per-share part of the model was fine. In a book with many small orders, the minimum is what adds up.
- One interest payment appeared three times in one data source and once in another. If the preferred source were ever missing and the job fell back to the other, interest would be double counted. So each line names which source it trusts.
None of these were strategy problems, but every one of them would show up in "live is worse than the backtest" and get read as the signal weakening.
Summary
- Define both lines first: same target positions, same pricing window.
- Split the gap into four parts that must sum to it; print the residual.
- Keep the pricing-time difference on its own line; it is not a loss.
- When data is missing, list it; never fill zero.
- Read execution together with unfilled positions.
This follows Why Backtests Fail in Live Trading: that post covers why to keep separate books; this one covers how to split the gap with nothing left over.