Specification · 003-position-engine

Real-time position engine

Net position and cost basis per instrument, folded incrementally from the ledger and revalued on every price tick — and always equal to a full replay.

Status Accepted Spec ID 003-position-engine Parent 001-portfolio-analytics Owner Brian Corbin · @briancorbinxyz Reviewers Brian Corbin · @briancorbinxyz Created Principlesv0.1.0 Smallest demoableTwo fills in an instrument produce the correct signed net quantity and VWAP cost basis; a price tick updates its mark-to-market value. Read time

The position engine folds the ledger into live per-instrument state: a signed net quantity, a volume-weighted average cost basis of the open exposure, and a mark-to-market value revalued on each price tick. Every update is incremental and O(1) per event, yet the result must equal a from-zero replay. It handles the awkward cases that break naive averaging — a position crossing through zero, and corrections arriving as compensating events — without special-case mutation.

I Independent
Depends on the umbrella model and the ledger (002) as its input stream. Weights and tax lots consume it, not the reverse.
N Negotiable
Outcome (correct signed net, VWAP basis, MTM), not a named data structure or accumulation order.
V Valuable
Unlocks SC-001 — the desk's live "what do I hold, at what basis, worth what" view.
E Estimable
An incremental fold with a known revaluation step; the sign-flip and correction cases are enumerable.
S Small
One fold, five functional requirements; realized-gain and weighting are deferred to siblings.
T Testable
Net qty, basis, and MTM all reconcile to replay; latency is directly measurable.

1 · Context

Position is the most-read number on the desk and the input to almost everything else — weights, exposure, and (with lot detail) realized gain. Recomputing it from the whole ledger on every fill is impossible at HFT rates, so it must be an incremental fold; but an incrementally-maintained average cost basis is exactly where subtle drift creeps in, especially when a position flips from long to short. This slice keeps the fold correct and fast.

It owns net quantity, cost basis, and mark-to-market revaluation. Turning positions into portfolio weights is 004; splitting basis into tax lots and realizing gain is 005.

2 · User scenarios & testing

Each story MUST be independently testable.

US1 · Net quantity and basis from fills P1

As a desk engineer, I want each fill to update the instrument's signed net quantity and average cost basis incrementally, so my position is live without a batch recompute.

Acceptance: after any sequence of buys and sells, net quantity equals the signed sum of fills and the basis equals the VWAP of the currently-open exposure; both match a full replay.

US2 · Mark-to-market on a tick P2

As a risk viewer, I want a price tick to revalue held instruments, so mark-to-market value and unrealized P&L are current.

Acceptance: a tick for a held instrument updates its MTM value and unrealized P&L in O(1); a tick for an un-held instrument is a no-op.

US3 · Consistent cross-instrument snapshot P3

As a weights consumer (004), I want all positions as of a single ledger sequence, so I never read a torn mix of pre- and post-event state.

Acceptance: a snapshot at sequence N reflects every instrument's state at exactly N — no instrument ahead of or behind another.

Edge cases

3 · Requirements

Conformance keywords (MUST, SHOULD, MAY) follow RFC 2119.

Functional

The engine MUST maintain a signed net quantity per (account, instrument), updated incrementally per fill, equal to the signed sum of fills folded from the ledger.

The engine MUST maintain an average cost basis (VWAP of open exposure) per position, updated incrementally, and MUST reset basis to the crossing fill when a position changes sign.

On each PriceTick the engine MUST revalue held instruments — mark-to-market value and unrealized P&L — in O(1) per held instrument, without touching quantity.

The engine MUST expose a consistent snapshot of all positions at a given ledger sequence, with no torn reads across instruments.

Corrections SHOULD be handled by folding compensating events, with no special-case mutation path distinct from a normal fill.

Non-functional

Per-trade position update MUST meet SLO-update, staying within the umbrella's tick-to-analytic envelope.

in-process wall-clock to fold one fill into net quantity and basis (good ÷ valid fills)

Per-tick mark-to-market revaluation MUST meet SLO-revalue.

in-process wall-clock to revalue one held instrument on a price tick (good ÷ valid ticks)

Net quantity and cost basis MUST reconcile to a full replay per SLO-position-reconcile.

daily incremental-vs-replay position reconciliations matching to the cent (matching ÷ total)

Out of scope (deferred)

  • Turning position market values into portfolio weights and concentration — the weights slice consumes positions.
  • Splitting the aggregate basis into individual tax lots and realizing gain on sells under FIFO/LIFO.
  • Splits, dividends, and symbol changes that adjust quantity or basis outside a trade — a later slice.
  • Translating non-base-currency positions into a common reporting currency.

4 · Data model

The engine's own state is a single row per open position; everything else it reads from the ledger.

Position
{ account, instrument, netQty (signed), basis (VWAP of open exposure), mtmValue, unrealizedPnl, asOfSeq }.
Revaluation input
The umbrella's PriceTick; drives mtmValue and unrealizedPnl only, never netQty.

Invariant: aggregate cost basis here is the position-level VWAP. The lot-level basis needed for realized-gain relief is a different decomposition and belongs to 005 — the two must agree in aggregate but are maintained separately.

5 · Success criteria

For any generated fill-and-tick stream, the engine's net quantity, VWAP basis, and mark-to-market value match a from-zero replay to the cent, including across at least one sign-flip and one correction.

A price tick revalues a held instrument and is a verified no-op for an un-held instrument, both within SLO-revalue.

6 · Assumptions

The ledger (002) delivers fills in gap-free sequence order; the engine relies on that ordering rather than re-sorting.

Instrument reference data (multiplier, currency) is stable within a session; corporate actions are deferred.

7 · Open questions

None outstanding — the sign-flip basis-reset rule and the read-consistency (snapshot-at-sequence) contract were settled in the interview; lot-level decomposition is explicitly out of scope, not open.

8 · Conformance index

Auto-built from every <spec-requirement> in this document.

9 · Change log

  1. Initial draft. Child of 001-portfolio-analytics: incremental net quantity (FR-001), VWAP cost basis with sign-flip reset (FR-002), per-tick mark-to-market (FR-003), consistent cross-instrument snapshot (FR-004), correction-as-fold (FR-005). Envelope: update latency (NFR-001), revalue latency (NFR-002), reconcile-to-replay (NFR-003).