Return Ledger

Log contributions, withdrawals and valuation checkpoints; get an approximated time‑weighted and money‑weighted return, computed entirely in this page.

Client-side · proof of concept
Date Flow Amount Fees Tax withheld Value after Note

"Value after" is only required for Valuation rows and the last row (today's value) — leave it blank on a Deposit or Withdraw row if your bank doesn't give you a historical value for that date. The first row's value defaults to its deposit amount if left blank. Fees and Tax withheld are optional costs deducted from a Deposit or Withdraw — leave them at 0 if none applied. Amount stays the gross figure (what actually moved in/out of the account, matching your statement); fees/tax only affect the separate net-of-cost XIRR below. Note is a free-text label purely for your own reference (e.g. "rent", "principal", "interest") — it never affects the calculations, and the filter above only changes which rows are shown here, never what's used in the results.

Performance

Portfolio value Deposit Withdrawal Dashed outline = no value entered, position estimated
How this is calculated

Time-weighted return (TWR): the ledger is split into sub-periods at each row where a value is actually known (a valuation row, or a deposit/withdrawal you also priced). Between two known values, Modified Dietz estimates the sub-period return from the start value, end value, and the dated flows in between, weighted by how long each was invested — no value is required at every single flow. The sub-period returns are chain-linked, then annualized over the full span. With only an opening and a closing value, this collapses to one Modified Dietz estimate for the whole period.

Money-weighted return (XIRR): every deposit and withdrawal is treated as a signed cash flow on its date, with the final value as a closing inflow; the annual rate that makes their net present value zero is found numerically (Newton‑Raphson with a bisection fallback). XIRR never needs interim values — only dated flows and the current value. If the first row is a valuation carrying a pre-existing balance rather than a deposit, that balance is treated as if it were invested on that date, since XIRR has no way to know when it actually went in — skipping it would understate the invested capital and wildly overstate the return.

Fees, tax, and gross vs. net: the Amount field always stays the gross figure — what actually moved in or out of the account, matching your broker statement. That's what TWR uses, so it keeps measuring market performance on its own, undistorted by what you paid in costs. Fees and tax you log on a Deposit or Withdraw only feed a second, separate figure: XIRR · net, computed on the amount minus fees and tax (what actually reached your pocket, or cost you extra on the way in). The gap between gross and net XIRR is the cost drag tile — roughly how many percentage points a year fees and tax are costing you. Tax withheld on unrealized gains inside valuation checkpoints isn't captured this way; only tax you actually record against a withdrawal is.

All data stays in this browser; nothing is uploaded, except a PDF you explicitly send to the AI import. Figures are only as accurate as the valuation checkpoints you enter.

Compare ledgers

Each ledger measured on its own flows and valuations · click a row to open it
Ledger Window Current value Gain / loss TWR ann. XIRR gross XIRR net

Ledgers aren't summed into a total: their valuations fall on different dates, so a combined figure would be mostly interpolation. Compare XIRR for "which did better for my money", TWR for "which asset performed better".

Yearly performance

YTD, and each calendar year your data covers
Year Window TWR XIRR gross XIRR net Start value End value Gain / loss