Specification · 001-portfolio-analytics

Real-time portfolio analytics

One append-only trade ledger; every number a deterministic fold over it. Positions, weights, and tax lots stay live at HFT tick rates and always reconcile to a full replay.

Status Accepted Spec ID 001-portfolio-analytics Owner Brian Corbin · @briancorbinxyz Reviewers Brian Corbin · @briancorbinxyz Created Principlesv0.1.0 Smallest demoableA trade and a price tick flow in; live position, portfolio weight, and open tax lot all update, and a full replay reconciles to the cent. Read time

This is the umbrella for a real-time position, portfolio-weight, and tax-lot platform sized for high-frequency trading: unlimited trade history, sub-millisecond tick-to-analytic latency, and answers that must always agree with a from-zero replay. The architecture is one decision — an append-only trade ledger as the single source of truth, with every analytic a pure deterministic fold over it — and everything domain-specific is carved out to four child slices: the ledger itself, the position engine, portfolio weights, and FIFO/LIFO tax lots. This spec owns only the shared event model and the cross-cutting reliability envelope that binds them.

I Independent
Depends only on the principles. Its children depend on it; it depends on none of them — it defines the shared model and defers all math downstream.
N Negotiable
Outcome (one ledger, deterministic folds, a bounded latency envelope), not a named database, language, or in-memory structure.
V Valuable
Unlocks SC-001 — a single trade produces coherent, reconcilable position, weight, and lot analytics in real time.
E Estimable
The umbrella is a model + envelope; each child is independently estimable from its own slice.
S Small
Nine requirements, no algorithms — the domain weight lives in the four children, each under its own budget.
T Testable
Every requirement is observable; the reconcile-to-replay criterion (SC-001) is a single mechanical check.

1 · Context

A trading desk needs three answers continuously and correctly: what do I hold (position), how concentrated am I (portfolio weight), and what is my realized and unrealized gain (tax lots). At HFT rates these must be maintained incrementally — recomputing from scratch on every fill is not an option — yet an incremental number that has silently drifted from the truth is worse than no number at all.

The failure mode this platform exists to prevent is divergence between the fast path and the truth. We close it structurally: a single append-only ledger of trades and price ticks is the only source of truth, and every analytic is a pure fold (reducer) over that ledger. The fast, incremental path and the slow, from-zero replay are the same function applied two ways, so they can always be reconciled. Stakeholders: the trading desk (consumers of the live numbers), risk (concentration and exposure), and finance (realized-gain tax reporting).

2 · User scenarios & testing

Each story MUST be independently testable: implementing only US1 still yields a viable MVP.

US1 · One trade, three coherent analytics P1

As a desk engineer, I want a single filled trade to update my live position, portfolio weight, and open tax lot together, so the three views never contradict one another.

Acceptance: appending one TradeEvent updates the position engine, the weight book, and the tax-lot book; a full replay of the ledger to the same sequence number reproduces all three to the cent.

US2 · Resume from a checkpoint P2

As an operator restarting a service, I want to restore analytics from a snapshot rather than replaying years of history, so recovery is bounded regardless of ledger size.

Acceptance: restoring from a snapshot at sequence N yields analytics bit-identical to a full replay through N; appending event N+1 proceeds normally.

US3 · Add an analytic without touching the ledger P3

As a platform owner, I want a new analytic to be a new fold over the existing ledger, so adding one never changes the source of truth or the other analytics.

Acceptance: a fourth analytic is introduced consuming only the ledger (and price ticks); no ledger contract change and no edit to the position, weight, or lot slices is required.

Edge cases

3 · Requirements

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

Functional

An append-only trade ledger MUST be the single source of truth for all analytics. Every analytic (position, weight, tax lot) MUST be a deterministic pure fold over the ledger and the price-tick stream — same input sequence, same output, no hidden state — so any analytic is reproducible by replay.

Rationale

Making the fast path and the replay path the same function is what makes NFR-002 (reconcile-to-replay) checkable rather than aspirational.

The platform MUST maintain position, portfolio-weight, and tax-lot analytics incrementally as trades and price ticks arrive — no full recompute on the hot path — while preserving the fold semantics of FR-001.

The platform MUST support snapshot and restore: a fold checkpoint at a ledger sequence number that, on restore, is bit-identical to a full replay through that sequence, so consumers bound cold-start without replaying all history.

Each analytic SHOULD be an independently versioned contract so a consumer can adopt positions without weights, or weights without tax lots, and each child slice can evolve on its own cadence.

Non-functional

End-to-end tick-to-analytic latency MUST stay within the real-time envelope defined by SLO-latency so downstream consumers see a fresh, coherent view at HFT rates.

wall-clock from a trade or price tick entering the platform to all affected analytics being updated, in-process (good ÷ valid events)

Every incremental analytic MUST equal a from-zero replay of the same ledger; divergence is a correctness fault governed by SLO-reconcile, not a tolerance to be tuned.

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

Given unlimited trade history, steady-state memory MUST stay bounded via snapshot and cold-tiering rather than growing with lifetime trade count, per SLO-memory.

resident hot-set bytes as a fraction of the 4 GB ceiling, sampled at peak (hot bytes ÷ ceiling)

Out of scope (deferred to child slices)

  • The durable append-only log itself — sequencing, replay, snapshotting, and unlimited retention live in the ledger slice.
  • Net position, average cost basis, and mark-to-market math live in the position engine.
  • Mark-to-market portfolio weights, the gross/net denominator, and concentration rollups live in the weights slice.
  • FIFO/LIFO lot relief and realized/unrealized gain live in the tax-lot slice.
  • Ingestion and normalization of the raw price-tick feed (venue quirks, dedup, gap-fill) — assumed upstream here.
  • Cross-account and cross-strategy netting of exposure — a later platform slice.

4 · Data model

This umbrella owns only the shared events every analytic folds over. Analytic-specific state (lots, weight books, position rows) belongs to the children.

TradeEvent
The atomic append: { seq, ts, account, instrument, side (buy|sell), qty, price, fees }. Immutable; seq is monotonic and gap-free. A correction is a new compensating TradeEvent, never an edit.
PriceTick
{ ts, instrument, price }. Drives mark-to-market revaluation for positions and weights; never changes a position's quantity.
Instrument
The traded symbol and its metadata (multiplier, currency). Reference data, assumed stable within a session.

Invariant: the state of every analytic at sequence N is a pure function of the ledger prefix [0, N] and the price ticks up to N. Nothing an analytic exposes may depend on wall-clock time or arrival order beyond the ledger sequence.

5 · Success criteria

An end-to-end run where one TradeEvent plus one PriceTick update position, weight, and tax-lot analytics within SLO-latency, and a full replay to the same sequence reconciles all three to the cent.

A new analytic is added as a fold over the existing ledger with zero changes to the ledger contract and zero edits to the position, weight, or tax-lot slices.

6 · Assumptions

A normalized price-tick feed is available upstream (deferred to TBD-market-data-feed); this platform consumes ticks, it does not clean them.

Analytics run in-process, co-located with the ledger's hot tail; the sub-millisecond envelope (NFR-001) is an in-process target, not a network round-trip budget.

7 · Open questions

None outstanding — the single architectural decision (append-only ledger, analytics as pure folds) and the split into four child slices were settled up front; each child resolves its own domain questions.

8 · Conformance index

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

9 · Change log

  1. Initial umbrella draft. Establishes the append-only trade ledger as the single source of truth, analytics as deterministic folds (FR-001/FR-002), snapshot/restore (FR-003), and the cross-cutting reliability envelope (NFR-001 latency, NFR-002 reconcile-to-replay, NFR-003 bounded memory under unlimited history). Carves the domain into 002-trade-ledger, 003-position-engine, 004-portfolio-weights, and 005-tax-lots.