How does a trading engine know what you own, what you paid, and what you have made? Kestrel answers with one small function.
Intro
One Question, One Function
Every trading engine must answer a deceptively simple question: what do I own, what did it cost me, and what have I made so far? Get that wrong, and every metric built on top of it is wrong too. In this episode we answer it with a single function of about twenty lines.
- What do I own?
- What did it cost?
- What have I made?
Where We Left Off
In the previous episode, the order manager normalised, checked and sent orders. But an order is only an intention. Today we deal with the consequence: a fill arrives, and the books must change. By the end you will be able to follow average cost through adds, reductions and flips, and explain why a future never drains your cash.
- Orders are intentions
- Fills change the books
- Goal: follow every rupee
The Book
A Book per Strategy and Instrument
PositionManager keeps a Book for every pair of strategy and instrument. Each Book holds four numbers: the signed quantity, the average price, the realized profit and loss, and the commissions paid so far. Separately, the manager holds one account cash balance. Two strategies trading the same instrument therefore never blur into each other.
- Key: strategy plus instrument
- Book: quantity, avgPrice, realized, commissions
- One account cash balance
Signed Quantity
Quantity is signed. A long position of ten units is plus ten, and a short of ten units is minus ten. A fill arrives with a side and a positive size, so the first move is to convert it: the sign of the side times the quantity. A buy of ten becomes plus ten, and a sell of ten becomes minus ten.
- Long is positive
- Short is negative
- q = sideSign(side) * quantity
Why Average Cost
There are several ways to match buys against sells. Kestrel uses average cost: all the units you hold share one blended price. That keeps each Book to a handful of numbers, with no queue of individual lots to maintain. It is also easy to reason about, and as we will see, it makes the accounting identity easy to test.
- One blended price
- Constant memory per Book
- Simple to verify
Average-Cost Mechanism
Three Cases
Every fill falls into one of three cases. If you are flat, or the fill has the same sign as your position, you are adding, so the average price is recomputed. If the fill has the opposite sign, you are reducing, and part of the position is realized. And if the reduction is larger than the position, the position flips to the other side.
- Add: re-average
- Reduce: realize profit or loss
- Flip: new average is fill price
Adding to a Position
When you add, the new average is a weighted mean. Multiply the old average by the old size, add the fill price times the fill size, and divide by the total size. Holding ten at one hundred and buying ten more at one hundred and ten gives two thousand one hundred over twenty, which is an average of one hundred and five.
- Weighted mean of prices
- (100*10 + 110*10) / 20
- New average: 105
Reducing: Realize, Do Not Re-average
When you reduce, the average price does not move. You are selling units you bought at that average, so each unit sold realizes the difference between the sale price and the average. Selling fifteen at one hundred and twenty against an average of one hundred and five realizes fifteen times fifteen, which is two hundred and twenty-five.
- Average stays at 105
- Per unit: 120 minus 105 = 15
- 15 units: realized 225
Closing and Flipping
Two special outcomes remain. If a reduction lands exactly on zero, the position is flat and the average resets to zero, because there is nothing left to average. If it crosses zero, you are now on the opposite side, and every remaining unit was opened at the fill price, so the average becomes that fill price.
- Lands on zero: avg = 0
- Crosses zero: avg = fill price
- Only the closed part is realized
The Realized Formula
The realized formula has four factors. The closed size is the smaller of the fill size and the position size. Multiply by fill price minus average, then by the sign of the old position, then by the instrument multiplier. The sign is what makes shorts work: a short profits when the fill price is below the average.
- Closed size: min(|q|, |p|)
- Direction: sign of old position
- Scale: instrument multiplier
Code Walkthrough
The Core of onFill, Part One
Now the code. The signed quantity q comes from the fill. We fetch the Book for this strategy and instrument, creating it if needed, take the old position p, and compute the new quantity as p plus q. We also prepare a realized delta that starts at zero, since adding realizes nothing.
- Signed fill quantity
- Book looked up by key
- realizedDelta starts at zero
The Add Branch
The first branch handles flat or same-sign positions. The absolute old size is treated as zero when it is below a small epsilon, which protects against floating point dust. Then the weighted average is applied. Notice that when the position is flat, the old average gets multiplied by zero, so a stale average can never leak into a new position.
- Epsilon guards float dust
- Flat: old average weighs zero
- Weighted mean of prices
The Reduce Branch
The else branch handles the opposite sign. Realized delta is the smaller absolute size, times price minus average, times the sign of the old position, times the multiplier m. It is added to the book. Then two checks: if the new quantity is within epsilon of zero, snap it to exactly zero and reset the average. Otherwise, if its sign differs from the old sign, the average becomes the fill price.
- Realize the closed part
- Flat: snap to zero, reset avg
- Flipped: avg = fill price
Updating Book and Cash
Finally, the book takes the new quantity and adds the commission. Then the cash rule splits by asset class. If the instrument uses variation margin, cash moves by the realized delta minus commission. Otherwise, cash goes down by the notional of the signed fill plus commission. How the commission itself is computed is the subject of episode eleven.
- Quantity and commissions updated
- Futures: realized minus commission
- Others: notional plus commission
Worked Example: Equity
The Four Trades
Let us run four trades on an equity-like instrument with multiplier one, and ignore commission. Buy ten at one hundred. Buy ten at one hundred and ten. Sell fifteen at one hundred and twenty. Sell ten at ninety. We will track quantity, average, realized and cash after each fill, and watch which branch of the code runs.
- Multiplier 1, no commission
- Four fills, three branches
- Track qty, avg, realized, cash
Trade One: Open
Trade one, buy ten at one hundred. The book is flat, so the add branch runs. Absolute old size is zero, and the average becomes one hundred. Nothing is realized. The equity branch of the cash rule runs: cash drops by ten times one hundred, which is one thousand.
- Add branch, from flat
- Average 100
- Cash: minus 1,000
Trade Two: Add
Trade two, buy ten more at one hundred and ten. The signs match, so the add branch runs again. The weighted average is one hundred times ten plus one hundred and ten times ten, over twenty, which is one hundred and five. Cash drops by another one thousand one hundred, so total cash is down two thousand one hundred.
- Same sign: add branch again
- (1000 + 1100) / 20 = 105
- Cash: minus 2,100
Trade Three: Partial Reduction
Trade three, sell fifteen at one hundred and twenty. Now the signs differ, so we reduce. The closed size is the smaller of fifteen and twenty, which is fifteen. Fifteen times one hundred and twenty minus one hundred and five gives two hundred and twenty-five realized. The position is now five, and the average stays at one hundred and five. Cash rises by eighteen hundred.
- Realized: 15 * 15 = 225
- Position 5, average still 105
- Cash rises by 1,800
Trade Four: The Flip
Trade four, sell ten at ninety. The old position is plus five, so the closed size is the smaller of ten and five, which is five. Five times ninety minus one hundred and five is minus seventy-five, a loss realized. The new quantity is minus five, whose sign differs from before, so the average becomes ninety. The remaining five units are a new short opened at ninety.
- Realized: 5 * (90 - 105) = minus 75
- New quantity: minus 5
- Average jumps to 90
Total Realized: 150
Add it up: two hundred and twenty-five minus seventy-five is a total realized of one hundred and fifty. Cash is up six hundred, but we also hold a short of five units. If the mark is ninety, those five units are worth minus four hundred and fifty. Cash plus holdings is one hundred and fifty above where we started, exactly the realized total.
- Cash: plus 600
- Short 5 at mark 90: minus 450
- Equity change: plus 150
Variation Margin
Futures Are Different
A futures contract is not bought for its full value. You post margin, and each day gains and losses are settled in cash through variation margin. So treating a futures notional as cash spent would be wrong. In Kestrel, isVariationMargin is true exactly when the asset class is Future. Equities and options still pay the full premium or notional.
- Equities, options: pay full notional
- Futures: only realized P&L moves cash
- isVariationMargin: assetClass == Future
Same Trades, Multiplier 65
Now replay the same four trades on a future with a multiplier of sixty-five. The quantities, averages and branches are identical. The only differences are the multiplier inside the realized formula, and the cash rule. The two opening buys move cash by nothing at all, since nothing has been realized.
- Same quantities and averages
- Multiplier 65 scales realized
- Opening buys: cash unchanged
Cash Only Ticks
Trade three realizes two hundred and twenty-five points, times sixty-five, which is fourteen thousand six hundred and twenty-five rupees added to cash. Trade four realizes minus seventy-five points, times sixty-five, which is minus four thousand eight hundred and seventy-five. Cash ends up nine thousand seven hundred and fifty higher, and that is the whole cash movement.
- 225 * 65 = 14,625
- Minus 75 * 65 = minus 4,875
- Net cash: plus 9,750
Equity and Marks
Marking the Position
Realized profit is only half the story. Open positions have unrealized profit, computed against a mark. The mark is the last bar close if there is one, otherwise the last fill price, and as a final fallback the average price itself. Unrealized is the mark minus the average, times quantity, times multiplier.
- Mark: last bar close
- Else last fill, else average
- (mark - avg) * qty * mult
The Equity Formula
summary computes account equity in two parts. For non-futures, it adds quantity times mark times multiplier, because you actually hold something with that market value. For futures, it adds only the unrealized profit, since you hold no asset value, only an accrued gain or loss that has not yet been settled. Then it adds cash.
- Cash plus holdings value
- Non-futures: qty * mark * mult
- Futures: unrealized only
The Tested Identity
Here is the invariant the tests enforce. Equity must equal starting capital plus realized plus unrealized minus commissions, within a tolerance of one millionth times the larger of one and the absolute capital. It holds for equities, for futures and for mixes. If any branch mishandles a sign or a multiplier, this identity breaks loudly.
- equity = capital + realized + unrealized - commissions
- Tolerance: 1e-6 * max(1, |capital|)
- Holds for equities and futures
Check the Future's Identity
Verify it for the future. After the four trades the position is minus five at average ninety. With a mark of ninety, unrealized is zero. Cash moved by plus nine thousand seven hundred and fifty, and futures add only their unrealized, so equity is capital plus nine thousand seven hundred and fifty. Realized is one hundred and fifty times sixty-five, exactly the same number.
- Unrealized at mark 90: zero
- Equity: capital + 9,750
- Move mark to 88: both sides +650
Settlement
Settlement Fills
What happens when a contract expires? Kestrel generates settlement fills, with the order id set to the constant kNoOrder and commission zero. These fills pass through the very same onFill. So an expiry closes a position with exactly the same average-cost and variation margin accounting as a normal trade, and the equity identity needs no special case.
- orderId is kNoOrder
- Commission is zero
- Same onFill, same accounting
Pitfalls
Pitfall: The Multiplier and Its Sign
The first pitfall is the sign. Forget to multiply by the sign of the old position, and every short earns the wrong direction. Forget the multiplier, and a futures profit is off by a factor of sixty-five. Both mistakes can look plausible in a single trade, but the equity identity exposes them when you test shorts and futures explicitly.
- Missing sign: shorts reversed
- Missing multiplier: wrong scale
- Test shorts and futures
Pitfall: Resetting the Average
The second pitfall is resetting the average on a partial reduction. Our example sold fifteen of twenty units and the average stayed at one hundred and five. Reset it, or recompute it from the remaining fill, and the later reduction realizes against the wrong base. Only a flat position resets to zero, and only a flip takes the fill price.
- Partial reduction: average stays
- Flat: average becomes zero
- Flip: average is fill price
Pitfall: Futures Notional as Cash
The third pitfall is counting futures notional as cash. A buy of a future at a price of one hundred with a multiplier of sixty-five would drain six thousand five hundred rupees from cash, though no such payment happens. Equity would look badly wrong at once, and the identity test would fail. The isVariationMargin split exists to prevent exactly this bug.
- Notional is exposure, not payment
- Wrong: cash drops by 6,500
- Right: cash moves by realized only
Recap
Recap
To recap. Each strategy and instrument has a Book of quantity, average price, realized and commissions, plus a single cash balance. A fill is signed, and then it adds, reduces, closes or flips. Equities and options pay notional into cash, while futures settle only realized profit. Equity equals capital plus realized plus unrealized minus commissions, and settlement fills use the same path.
- Book per strategy and instrument
- Add, reduce, close, flip
- Futures: variation margin
- Identity holds always
Next: Where Fills Come From
Everything here assumed that fills simply arrive. In a backtest, though, something has to decide when and at what price an order fills, without peeking at the future. That is the simulated venue, and it is the subject of the next episode. This is educational material, not financial advice, and all numbers were illustrative.
- Next: the simulated venue
- Fills without lookahead
- Subscribe for episode 10