A single-file, zero-dependency web app for tracking personal investment portfolios, computing money-weighted returns, and projecting retirement sustainability.
No server. No login. No data ever leaves your browser.
Disclaimer: This tool is provided for informational and educational purposes only. It is not financial advice. Past returns do not guarantee future results. The projections produced by this tool are mathematical illustrations based on inputs you provide — they are not predictions. Do not make investment decisions based solely on this tool. The author bears no responsibility for financial outcomes resulting from use of this software.
BSD 3-Clause — see LICENSE
An individual investor whose portfolio grows through irregular, real-world deposits needs an easy way to see the evolution of their investments.
This tool computes the Money-Weighted Rate of Return (MWRR), which answers the question that actually matters:
Given exactly when and how much I invested, what annualized return does my portfolio imply?
It then strips out inflation to give you a real return, and runs a simulation to tell you whether your projected retirement portfolio is sustainable at your target withdrawal rate.
For a detailed guide on how to use and interpret the numbers, see docs/GUIDE.md
To understand the math supporting this tool, see docs/MATH.md
To see the changes along different versions of the tool, see docs/CHANGELOG.md
For a map of the code structure and function descriptions, see docs/CODE.md
The app is a 4-step wizard. Each step unlocks the next.
Upload an Excel file containing your deposit history. The mapper UI lets you match your file's column names to the required fields (date, amount, currency, portfolio name). ARS deposits are converted to USD using the exchange rate column if provided.
Deposits are stored in sessionStorage — they exist only for the duration of the browser tab and are wiped automatically when the tab is closed. Data never leaves your machine.
The Excel file is source of truth #1. You decide which cash flows are in scope. The tool does not validate financial reality — it only ensures the math is correct. What goes into the spreadsheet, and what stays out, is entirely your responsibility.
Tag annotations: any row whose note begins with tag: (case-insensitive) is treated as a chart marker. In Step 2, a vertical dashed line is drawn on the historical growth chart at that date, in the portfolio's color, with the tag text as a label. Use this to annotate significant events — strategy changes, market events, capital injections — directly on the history chart.
| Figure1: Loading of deposits and withdrawals |
Each portfolio is a row in a table. Enter the current market value (VAL) directly in the table. Returns calculate automatically. Change the VAL or the inflation assumption and the rates update instantly.
The broker-reported VAL is source of truth #2. The tool has no connection to any broker or external service. Whatever market value your broker shows, you enter it here. The tool trusts it unconditionally.
Inflation assumption: enter the annual inflation rate (%) you consider representative of both your investment history and your planning horizon. This single rate serves two roles: it converts your historical
Since all deposits are converted to USD, the relevant benchmark is always US CPI regardless of your country of residence.
| Assumption | When to use |
|---|---|
| 2.5% | Market-implied US CPI (10-year TIPS breakeven); appropriate if you weight recent forward-looking expectations |
| 3% | Long-run US CPI average (post-WWII); standard base case |
| 4–4.5% | Conservative stress-test; reflects the high-inflation decades of 1965–1990 |
| > 4.5% | Extreme stress-test scenario |
A higher inflation assumption lowers the computed real return. When in doubt, run the tool twice (once at 3%, once at 4.5%) to bracket the outcome.
Each row shows: nominal r, real r, quality badge, deposit count, total deposited, gain, gain/dep, share of combined VAL, max lifetime, weighted average time, and status (Ongoing / Completed).
A combined row appears at the bottom once all portfolio VALs are entered and shows aggregated metrics across all portfolios.
Once at least one VAL is entered, a historical growth chart appears below the table. For each portfolio with a solved return, it plots the implied portfolio value from the first deposit date to today by compounding each deposit at tag: appear as labeled vertical dashed lines on the chart, drawn in the portfolio's color, with the tag text as a word-wrapped label.
| Figure2: Nominal and Real rates |
Configure a time horizon, an optional alternate real return for scenario comparison, and your current age. The app projects each portfolio using its own nominal and real MWRR, plus a combined row.
A compact table shows each portfolio as a row with columns: nominal r · real r, monthly deposit (editable), new deposits over the horizon, Nominal VAL, Real VAL, and Alternate Real VAL (when an alternate rate is set). The combined row appears at the bottom.
The app projects:
- Nominal VAL — future value at the nominal MWRR
- Real VAL — future value in today's purchasing power
- Alternate Real VAL — same projection at a user-specified alternate real return, for scenario comparison
A growth chart overlays all three curves over the chosen horizon, with the x-axis labelled by actual age.
Below the chart, a sustainability simulation asks: after the horizon ends, how much do you withdraw monthly? It computes whether the portfolio survives indefinitely or depletes, and if so, at what age.
An optional NewRealRate (nrr) field lets you specify a different real return that applies only from the horizon date onward — modelling a conservative asset swap at retirement (e.g. a growth allocation during accumulation → a conservative allocation during withdrawal). The accumulation projection is unchanged; only the withdrawal simulation uses nrr. When set, a separate nrr row appears in the sustainability table and a purple dashed curve is added to the sustainability chart. The alternate real return (arr) line is unaffected.
An optional NRR delay (yr) field defers the switch by N years after the horizon. During the delay the simulation continues at rr; after the delay it switches to nrr. The perpetuity shown in the nrr row is based on the balance at the moment of the switch, not at the horizon. Delaying the switch allows extra years of compounding at the higher growth-phase return before locking into the conservative allocation — at the cost of additional sequence-of-returns risk.
| Figure3: Projection |
| Figure4: Withdrawal |
Once a monthly withdrawal is set in Step 3, the Summary step unlocks. It produces a self-contained, printable report with no interactive inputs.
Three read-only tables snapshot the current state:
- Returns — same columns as Step 2: nominal r, real r, quality, deposits, gain, max life, weighted avg, status. A one-liner above the table shows the inflation assumption used.
- Projection — same columns as Step 3: rates, VAL today, monthly, total deposits, Nominal VAL, Real VAL, Alternate Real VAL. A one-liner above the table shows the age and horizon used.
- Withdrawal — real VAL at horizon, perpetuity, monthly withdrawal, age out of money, years of withdrawal; main (
rr), NewRealRate (nrr, if set), and alternate (arr, if set) scenarios. A one-liner above the table shows the age at retirement.
A Hide $ toggle button (top-right of the panel, next to Print) replaces all dollar and monthly amounts in the three tables with *** and suppresses the timeline chart Y-axis values. Rates, percentages, and time values remain visible. This lets you share a screenshot or PDF of the summary without exposing absolute portfolio values. The setting persists within the session.
Below the tables, a full timeline chart connects all three phases on a single age axis:
- History (first deposit → today) — combined portfolio value compounded at the nominal MWRR, plus a cumulative deposits reference line.
- Projection (today → retirement) — real VAL and alternate real VAL starting from today's combined VAL, with the deposits line continuing through planned monthly additions. Nominal curve is omitted: the real line is what matters for retirement planning.
- Withdrawal (retirement → depletion) — real VAL and alternate real VAL drawn under the monthly withdrawal simulation. Vertical dashed lines mark the "Today" and "Retire" boundaries on the chart.
A Print / PDF button triggers the browser's print dialog. A print stylesheet hides all navigation and interactive panels, leaving only the Step 4 content for clean output. The timeline chart is redrawn with a light-theme palette (white background, dark lines) before the print dialog opens, then restored to the dark screen theme afterwards.
| Figure5: Summary |
- Single HTML file — no build system, no npm, no dependencies to install
- Bundled libraries: Chart.js 4.4.1 (MIT) and SheetJS 0.18.5 (Apache 2.0) — no CDN, no external requests
- State persisted in
sessionStorageunder keyspt5_deps,pt5_vals,pt5_rs,pt5_monthly— cleared automatically on tab close - Fully bilingual: English and Spanish (Argentine locale)
- Supports
.xlsx/.xlsfiles - Currency: USD native; ARS converted via per-row exchange rate
| Field | Required | Accepted column names |
|---|---|---|
| Deposit date | Yes | date, fecha, fecha_deposito, transaction_date |
| Deposit amount | Yes | deposit_value, amount, monto, valor, importe |
| Currency | Yes | currency, moneda, currency_code |
| Portfolio name | Yes | portfolio, cartera, account, cuenta, fund |
| USD exchange rate | No | exchange_rate, tipo_cambio, fx, fx_rate, rate, tc |
| Note | No | nota, description, descripcion, comment |
Currency values accepted:
- USD:
USD,US$,U$S,Dollar,Dolares,Dólar - ARS:
ARS,AR$,Pesos,Peso
Date formats accepted:
| Format | Example | Notes |
|---|---|---|
| Excel date cell | (native) | Read directly as a Date object by SheetJS — most reliable, no ambiguity |
YYYY-MM-DD |
2021-12-13 |
ISO 8601; always unambiguous |
| ISO with time | 2021-12-13T00:00:00.000Z |
Time component stripped automatically |
DD/MM/YYYY |
13/12/2021 |
Argentine default; /, -, and . separators accepted |
MM/DD/YYYY |
12/13/2021 |
Detected automatically at the file level (see below) |
MM/DD vs DD/MM detection: the tool scans all dates in the file before importing any row. If any date has a second component greater than 12 (e.g. 10/31/2025 — month 31 is impossible), the entire file is treated as MM/DD/YYYY. This means ambiguous dates in the same file, such as 3/6/2024, are correctly resolved to the same format (March 6, not June 3). If no unambiguous date is found, the file defaults to DD/MM/YYYY.
Using native Excel date cells (not text strings) is the most reliable option — SheetJS reads them directly as Date objects with no parsing ambiguity.
Each portfolio in Step 2 displays a badge based on its real return, not the nominal return. Nominal return is misleading as a quality signal because it includes inflation — a portfolio earning 8% nominal in a 10% inflation environment is actually losing purchasing power.
| Real r | Label | Color |
|---|---|---|
| > 5% | Excellent | Green |
| > 2% | Good | Blue |
| > 0% | Fair | Yellow |
| ≤ 0% | Poor | Red |
A portfolio with a high nominal r but negative real r will correctly show as Poor.