← Blog

Live vs Research P&L: Split the Gap Into Four Parts That Add Up Exactly

October 26, 2026

中文

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

Research net R_nettarget shares · modeled cost−Live LWholdings after fills · actual cost=Unfilled positions(target − actual holdings) × price moveExecutionfill vs decision price − modeled slipCommissionactual − modeled commissionBorrowactual − modeled borrow+++Same pricing window for both lines; residual = parts − gap, must be 0

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:

PartWhat it measuresWhere to look
Unfilled positionsP&L on shares you meant to hold but didn'tRejections, partial fills, untradeable names, extra non-target holdings
ExecutionFill price vs decision price, against modeled slippageOrder timing, order type, liquidity
CommissionWhat the broker charged vs the modelPer-order minimums, fee tiers, split orders
BorrowActual short borrow cost vs the modelHard-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:

Example: where the 105.48 dollar gap comes fromResearch R_net −356.48 · Live LW −251.00 · Gap −105.48 (USD)Unfilled positions−90.00Execution−3.60Commission−1.40Borrow−10.48Sum of parts−105.48, residual 0Pricing-time difference+123.50Pricing-time difference sits on its own line: not a loss, not one of the four parts

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.