Part 6 · 2 chapters · ~20 min
Portfolio and Statements
Statements as snapshots with an explicit boundary and version, totals and running balances computed server-side and never summed on the client, reloads reproducible by an as-of in the URL, exports from the same query, aggregations that share the ledger, and a portfolio view where every number names its price source and time.
16
Statements that are correct under reload
a snapshot with a boundary
- The boundary: entries with a booking date in the period, as of the ledger at generation time; a back-dated entry (settled in October, value-dated September) appears in a regenerated statement with a marker, and the previously issued statement is kept as a numbered version. The CBA module's event log makes any version reproducible; the client shows the version and the as-of time.
- Server-side totals: opening, closing, total in and out, fees, interest, from the same query that produced the rows. The client never sums rows it holds: a page has not got them all, a filter has excluded some, and a float sum is wrong anyway (part 4). The rows are evidence, not the source.
- Running balance: per row, server-side, in booking order; page 2 starts where page 1 ended; any other sort drops the column and says so. Infinite scroll (the FSD course M6) for browsing; explicit pages that match the PDF for the document.
- Reload: the URL carries period, page and as-of; the same query returns the same page; entries that arrived since do not shift the pages and are offered as "3 new entries since this view: refresh" (M1's cursor stability, with time explicit).
- Exports from the same query and as-of, with the same totals, order, locale and sign convention stated in the header; never from the rendered table. Large exports are jobs with progress (M1 v4). A difference between the screen and the PDF is a dispute.
- Aggregations (spend by category, monthly totals, charts) from the same ledger with versioned category rules and stated period boundaries; the chart renders the aggregation endpoint's numbers and never re-sums (M4). A chart total that disagrees with the statement by a kobo is a ticket.
A STATEMENT THAT IS CORRECT UNDER RELOAD
a snapshot with a boundary, server-side totals, and a document that matches the ledger to the minor unit
swipe the figure sideways, or tap expand for full screen
1/6
the boundary
The boundary: a statement for September is "entries with a booking date in September, as of the ledger at the time of generation". An entry that settled on 2 October with a value date in September is a back-dated entry: it appears in a re-generated September statement with a marker, and the previously issued statement is kept as a numbered version. The CBA module's event log makes both reproducible; the client shows the version and the as-of time.
17
The portfolio view
every number is a function of a price and a time
- Positions: quantity with the instrument's own exponent (integer shares, eight-decimal crypto, fractional fund units), average cost by the jurisdiction's method (FIFO, average, specific lot), current price with its kind and age, market value, unrealised gain as money and percent. Server-computed; client-formatted.
- The total: cash plus market values at one price snapshot so every position is valued at the same instant, with that time shown. A client summing positions valued at different ticks shows a total that never existed. With live prices the server recomputes on a cadence and pushes the total with its version and time.
- Realised gains for the period from the lots sold, by the method, with fees; separate from unrealised, never mixed into one unlabelled "gain"; the method named on screen so a user comparing brokers knows why numbers differ. It must agree with the statement and the tax document.
- Charts: end-of-day totals (server-side aggregation, M4) with deposits and withdrawals marked, because a value that rose by a deposit did not gain; time-weighted or money-weighted return chosen, named and explained once.
- Live: a price event (M8) updates the rows that hold the instrument and the total, by version; a subtle indicator per change, never a flash per tick (part 4); the total shows "updating" then its new time; a disconnected feed turns ages amber and the total "as of". Nothing pretends to be current.
- Under reload: a live view fetches the latest snapshot; a pinned view (the as-of in the URL) returns the same numbers; the export comes from the snapshot on screen with its time in the header. A portfolio export without a time is a number with no meaning.
the exercise
Export your product's statement for last month as PDF and CSV, then screenshot the screen. Compare the three closing balances and the three totals. Any disagreement is one of the six rules above, unapplied.
THE PORTFOLIO VIEW
positions, cost basis, unrealised and realised, and a total that names its price source and time
swipe the figure sideways, or tap expand for full screen
1/6
positions
Positions: instrument, quantity (with the instrument's own exponent: shares are integers, crypto has eight decimals, funds have fractional units), average cost (by the method the jurisdiction requires: FIFO, average, specific lot), current price (its kind and age), market value, unrealised gain as money and as a percentage. Each a server-computed field; the client formats.