Quant Python: Architecting Autonomous Trading Systems

Quant Python: Architecting Autonomous Trading Systems

Day 81 — Realized P&L: Locking In Profits After Closure

Python Quant's avatar
Python Quant
Aug 09, 2026
∙ Paid

The “Just Subtract the Averages” Trap

Every engineer who’s built a position tracker eventually writes this line:

realized_pnl = (exit_price - avg_cost_basis) * qty_closed

It’s clean. It compiles. It passes your first three manual tests. It is also wrong the moment your book does anything more sophisticated than “buy once, sell once” — and on a real multi-symbol book, that’s most of your fills.

The bug is conceptual, not syntactic: avg_cost_basis is a mutable, blended number. Every time you add shares at a new price, you recompute a weighted average and throw away the individual lots that produced it. That’s fine for unrealized P&L, where you genuinely want a single blended cost basis against the current mark. It is not fine for realized P&L, because realization is a discrete event tied to a specific tranche of shares leaving the book — and once you’ve blended those tranches together, you can no longer answer the question “which shares, bought at which price, did this specific sell actually close?”

You need that answer for three reasons that have nothing to do with code style: tax-lot accounting (FIFO/LIFO/specific-ID all require lot identity), P&L attribution (a strategy that scales into a position across five fills needs to know which fill’s edge decayed), and audit trails (a regulator or an LP asking “show me the trade that produced this $14,200 gain” wants a lot, not an average).

The Failure Mode

Here’s where average-cost bookkeeping actually breaks, concretely. Say your PositionTracker (Day 78) holds 10 shares of AAPL at a blended average cost of $200.00 — built from a 5-share lot at $195 and a 5-share lot at $205. A sell of 5 shares at $210 comes in. The average-cost approach reports:

realized = (210 - 200) * 5 = $50.00

But FIFO says the sell closes the first lot — 5 shares at $195 — not a blended slice of both:

realized = (210 - 195) * 5 = $75.00

That’s a $25 discrepancy on a single 5-share trade, and it compounds. Run this drift across a day with a few hundred partial fills on a mean-reverting strategy that’s constantly scaling in and out, and your “realized P&L” line on the EOD report (Day 77) stops being a source of truth. Worse: average-cost bookkeeping has no concept of a long-to-short flip within a single fill — sell 8 shares against a 5-share long and you’ve both closed a position and opened a new short, in one atomic event, and a scalar average can’t represent “I am now short 3 shares at $210” without a second, separate mutation that a crash can catch you between.

The AutoQuant-Alpha Architecture

User's avatar

Continue reading this post for free, courtesy of Python Quant.

Or purchase a paid subscription.
© 2026 Python Quant · Privacy ∙ Terms ∙ Collection notice
Start your SubstackGet the app
Substack is the home for great culture