The portfolio engine
A collection is a ledger, not a spreadsheet cell. Every copy that arrives or leaves is one append-only row; cost basis is FIFO over acquisition lots; and the auction house feeds it by derivation rather than by hooks in the settlement path.
Why a ledger#
The naive design stores a quantity and a cost on a row and updates them in place. It fails the first time anything is corrected, because there is no longer any record of what it was corrected from, and it fails silently, which is worse.
Here, every arrival, departure and cost is one append-only row. The quantity, cost and realized-gain figures on the position are caches, maintained in the same transaction as the row that changed them and re-derivable from the rows at any time. An invariant checker re-derives them and compares, the same way the accrual book is verified. Corrections are reversal rows that name what they reverse. Nothing edits history.
Positions#
A position is one card in one physical state.
- A certified slab is its own position, one certificate, one physical object, never merged with anything.
- Raw copies of the same card merge into one position with a quantity.
- A certificate number without a grading company is refused, because a certificate is meaningless without the house that issued it.
Where a copy physically is is not a property of the position. The register reads custody from the vault's own ledger, never from a location note its owner typed, and a card moving into or out of the house's keeping books nothing here: custody is not a transaction. The vault.
Row types#
| Class | Types | Effect |
|---|---|---|
| Acquisitions | purchase, auction-purchase, buynow-purchase, trade-in, gift-in, adjust-in |
Copies arrive and open an acquisition lot carrying their total all-in cost. |
| Disposals | sale, consignment-sale, trade-out, gift-out, adjust-out |
Copies leave, consuming basis FIFO from the oldest lots first. |
| Realizing | sale, consignment-sale, trade-out |
A subset of disposals. A gift or an adjustment carries basis out rather than booking a gain, because giving a card away is not a loss event. |
| Costs | grading-fee, cost |
Attach to copies still held, and are carried out by FIFO with those copies. |
Cost basis#
FIFO over acquisition lots. A lot stores the total all-in cost of its copies rather than a per-copy figure, and consuming k of n copies takes the difference:
basisOut = floor(T * (used + k) / n) - floor(T * used / n)Storing a per-copy cost and multiplying loses cents to rounding, and the loss compounds: sell three of four copies of a $100 lot at $25 each and the fourth carries $25, but sell them from a lot of three at $33.33 and a cent has evaporated. Taking the difference of two floors makes the terms telescope: the sum of every draw from a lot is exactly T, whatever order and however many copies leave. It is the same no-lost-cents discipline the fee module uses.
Consumed basis is a stored fact on the lot, never re-derived from the rows, so a correction elsewhere cannot silently change what a past disposal took.
Derivation, not hooks#
The auction house does not write into the portfolio. The portfolio reads what the auction house already committed.
| Source | Becomes | Reference key |
|---|---|---|
| A paid invoice | An acquisition, exactly once | invoice:<id> |
| A settled consignment | A disposal, exactly once | consign-sale:<lotId> |
| A sale the house unwinds | A reversal, automatically, on the next pass | Names the row it reverses |
The reference key is unique, which is what makes "exactly once" a property of the database rather than of the code that happens to run. The sync is idempotent: run it twice, run it after a crash, run it after a restore, and the ledger is the same.
The money-critical write paths (closing a lot, settling an invoice, unwinding a sale) stay untouched. A bug in the portfolio engine cannot corrupt a settlement, cannot double-charge an invoice, and cannot block a close, because it is downstream of all of them and holds no hook inside any. The worst a portfolio bug can do is produce a wrong portfolio.
The unwind that needed a second pass#
A consignment sale the house unwinds after settlement needs a pass of its own, because settling hands the copy back to the seller's side of the ledger and the append-only rule means the original consign-sale:<lotId> row still exists and still names that sale. Without the second pass the unwind stopped at the buyer: the desk refunded them, and the consignor's ledger went on showing a disposal that had been undone.
Guards#
The register refuses input that would make its own arithmetic meaningless rather than storing it and producing a confident wrong total:
- $50M per money field, mirroring the bid ceiling.
- A hard ceiling above which sums stop being trustworthy. No register holds a trillion dollars, and a typo that says it does poisons every aggregate on the page.
- No purchases dated before the hobby existed.
- One certified slab is one physical object. The register refuses a quantity above one on a certified position.
- A certificate needs its grading company.
Valuation#
The price signal is the house's own settled sales, and nothing else on this deployment. The method admits two weaker kinds of evidence and keeps both visibly apart from a sale: a licensed market index, which never blends with sales and never dilutes them, and a desk override, which carries a reason. Where no index source is enabled — the state this deployment starts in (see below) — every estimate rests on Minthouse sales or on an override the desk signed for.
- One sold lot becomes one observation, under exactly the eligibility rules the public prices-realized page applies: sold, a real price, no voided invoice, no unwind entry. The portfolio can therefore never quote a sale the results page would refuse to publish.
- The price recorded is what the buyer actually owed: hammer plus that invoice's own premium, which is zero for a fixed-price Buy Now.
- Ingest is a read of the auction tables on its own timer, idempotent under a unique per-lot fingerprint. A sale that later unwinds or defaults flips its observation to void on the next pass.
The estimate#
A recency-weighted median over the eligible observations of one card in one physical state, with MAD outlier exclusion once there are enough points to know what an outlier is.
- No observations means "insufficient", never a number.
- A raw card is never priced off a slab.
- Every estimate stores the observations, weights and exclusions that produced it, so "why this price?" is a lookup rather than a shrug.
A median rather than a mean because a single outlier sale (a shill, a fat-finger, a private arrangement run through the room) moves a mean and does not move a median. Recency weighting because a card's market two years ago is evidence about that market, not this one.
External price providers#
None are wired. The adapter contract exists and everything downstream (deduplication, valuation, series, staleness) already handles external rows; wiring a provider is a credentials-and-terms decision rather than an architecture change. Until one is wired, every number in the portfolio traces to a Minthouse sale.
What the register reports#
Four figures, and one identity between them. The vocabulary is fixed because the same words on different screens meaning different things is how a collector comes to distrust all of them.
| Figure | Exactly what it sums |
|---|---|
| Remaining cost basis | The basis still sitting in the copies held. Basis that left with a disposal is gone from it. |
| Unrealized P&L | Value minus basis, over priced positions only. A position with no market evidence contributes nothing to it. |
| Realized P&L | Every realizing row, lifetime, excluding rows a reversal has cancelled. Proceeds net of the fees, shipping and tax on that row, less the basis FIFO took out. |
| Total P&L | Realized plus unrealized. Nothing else is added, and the coverage of the unrealized half is disclosed beside it. |
The same figures can be read day by day rather than only as of now, which is what makes a period readable instead of asserted: each stored day carries that day's value, remaining basis, flow, quantity, priced quantity and cumulative realized P&L. Total P&L on a given day is therefore what the copies then held were worth over their remaining basis, plus everything realized up to that day, and a period's realized P&L is the difference between its two endpoints. Observations are daily; no intraday figure is ever invented from one.
Coverage, and why nothing is carried at zero#
A register whose owner holds cards the house has never sold has positions it cannot price. The choice of what to do about those decides whether every total on the page is honest.
Carrying it at zero would understate the collection by the price of every card without comparable sales. Carrying it at a guess would put a number the house cannot defend into a figure a collector might act on. So it is carried at what was paid, it contributes nothing to unrealized P&L, and the page says how much of the headline is which kind of evidence.
The composition is published beside the total rather than buried: how much of the value comes from the house's own settled sales, how much from a licensed market index where one is enabled, how much from a desk override, and how much is simply cost carried forward. Positions are also counted by the confidence of their estimate, and an estimate whose newest evidence is older than 90 days is flagged as stale rather than quietly reused as current.
Market movement is not flow#
A collection that grows by $4,000 because its owner bought a card for $4,000 has not performed. Separating the two is the whole reason the daily series stores a flow column beside the value.
- The movement line accumulates each day's change in value with that day's flow taken back out. It is dollars of market, not dollars of shopping.
- An acquisition's flow is what was paid, all in. A cost row, such as a grading fee, flows at the fee.
- A realizing sale's flow is the money that left, meaning net proceeds, not the basis that went with it. Booking the flow at basis printed the sold copies' unrealized gain as a market loss on the day of a profitable sale, which is exactly backwards.
- A gift or an adjustment has no proceeds, so it flows at basis. A regrade, which moves a copy between two positions of the same collection, flows nothing at all: each side lands at its own segment and nothing entered or left.
Attribution: why a period moved#
The same doctrine, per position, over a window the reader picks. Market contribution is computed over the copies held at the period start, so a card bought halfway through the period contributes nothing on its arrival day and cannot flatter the period it arrived in.
| What it answers | How |
|---|---|
| Which cards moved the number | Price change across the window multiplied by copies held at the start, named, largest movers in both directions. |
| What arrived | Count, copies and all-in cost of acquisitions inside the window, reversals excluded. |
| What was realized | Count, copies, net proceeds and realized P&L of disposals inside the window. |
| What the house learned | Positions that gained pricing coverage inside the window, and positions that lost it. A total that moved because evidence appeared is not a market move, and the page says so rather than absorbing it silently. |
| What happened physically | Vault events in the window by kind: cards verified into custody, listings that went live, ownership changes. The vault. |
Reading it at scale#
The register's header used to walk every position with a valuation lookup each, which is correct arithmetic and one query per card: at ten thousand positions it was twenty thousand queries and nine megabytes to draw one summary card. The figures above are now single aggregation passes, the holdings list is a page filtered, sorted and counted in SQL, and the daily series rebuilds incrementally from the earliest day the ledger or the price observations actually dirtied. Where the estimate clock has moved for a reason the incremental pass cannot account for, it falls back to a full rebuild rather than trusting itself.
What a portfolio figure means#
Cost basis here is a bookkeeping convenience computed FIFO. Whether FIFO is the right method for a particular holder in a particular jurisdiction is a question for their accountant, and the method is documented above precisely so that the accountant can see what was done rather than having to reverse-engineer it. An estimate is a recency-weighted median of past sales, not an appraisal, not an offer, and not a floor.