Upload your own custom return series
How to upload a CSV or .xlsx of historical returns and use it in your simulation.
FIREproof ships with a curated catalog of historical-returns series (S&P 500, US Total Market, MSCI EAFE-style developed-markets index, 10-year Treasury, etc.). If you have your own historical-returns data that you have the right to use, you can upload it as a custom return series and reference it from any account just like a built-in series.
What the file should look like
The uploader expects a generic year-down-the-side, codes-across-the-top layout. CSV and .xlsx are both supported, and the parser can pick out monthly or annual rows, and either per-period returns or raw price levels.
The simplest example uses the real S&P 500 composite that FIREproof already ships with. Here is what 20 years of that data would look like in a spreadsheet ready for upload, with column A as the year column and a single series column for the S&P 500 total return. The first four rows are the header rows the importer reads to set up the series, and the data starts on row 5.
| A | B | |
|---|---|---|
| 1 | code | SP500_TR |
| 2 | name | S&P 500 Composite |
| 3 | expense ratio | 0.0003 |
| 4 | first usable year | 1990 |
| 5 | 2004 | 10.9722 |
| 6 | 2005 | 5.2418 |
| 7 | 2006 | 12.2299 |
| 8 | 2007 | 4.4337 |
| 9 | 2008 | -40.6741 |
| 10 | 2009 | 26.5304 |
| 11 | 2010 | 11.8113 |
| 12 | 2011 | 0.1442 |
| 13 | 2012 | 14.3945 |
| 14 | 2013 | 27.1035 |
| 15 | 2014 | 13.6350 |
| 16 | 2015 | -0.0092 |
| 17 | 2016 | 9.3740 |
| 18 | 2017 | 18.5927 |
| 19 | 2018 | -3.6418 |
| 20 | 2019 | 23.7385 |
The colored cells correspond to the mapping fields the importer reads to set up your series.
- Code is a 1 to 64 character identifier (letters, digits, underscore, hyphen). This is the name you'll pick from in account allocation.
- Name is a friendly label.
- Expense ratio is decimal (
0.0003= 3 bps). The simulation subtracts this per year. - First usable year is the earliest calendar year you trust the data. Older years fall back to the public proxy (
sp500_compositefor equity,ust_10yfor bond). - First data row marks where year-by-year returns begin. Everything above this row is header metadata; everything from this row down is treated as data.
- Year column holds 4-digit years, and the data values live alongside them.
Returns can be in either percent (15.298) or decimal
(0.15298), and the importer picks the right one
automatically based on the magnitudes it sees. You can also upload
price levels (NAV or index level) and FIREproof will compute the
annual returns from December-to-December changes. Monthly data is
supported too; the importer chain-links monthly returns into one
annual return per calendar year.
You can put many series side by side in one file, with each column representing a different series. PortfolioVisualizer "By Year" backtests, Bogleheads workbooks, and any spreadsheet you've built yourself will all work as long as they follow this general shape.
Granularity and value type
The upload modal asks two questions in the mapping step that shape how your file is interpreted:
- Granularity: Annual means one row per
calendar year; Monthly means one row per month
(
MM/YYYY,YYYY-MM, orYYYY-MM-DD). Monthly inputs are aggregated to one annual return per calendar year before storage. Years that are missing the December anchor (for price levels) or all 12 months (for returns) are skipped with a warning shown in the success toast. - Value type: Returns means each cell is
a per-period percentage or decimal return. Price levels
means each cell is a price / NAV / index level. The importer
computes the return for each year as
(price_end_of_year / price_end_of_prev_year) − 1. The first year of a price series has no baseline and is dropped.
Both fields auto-detect from the file: a MM/YYYY column
flips Granularity to Monthly; values that are always positive and
well above the return-magnitude range (no negatives, no values
near zero) flip Value type to Price levels.
Common sources
- Hand-built CSV. Year column in A, codes across row 1, returns in the grid below.
- PortfolioVisualizer exports. Save the "By Year" backtest as CSV.
- Bogleheads-community spreadsheets. The Bogleheads wiki curates a number of public historical-return workbooks; if your copy has cached calculated values (which Excel writes by default), you can upload the .xlsx as-is.
Using your custom series in a simulation
- Open the Account Allocation Picker in any account's Edit modal.
- Scroll to Your custom equity series or Your custom bond series. Your uploaded codes appear with a "Custom" pill.
- Pick one. The new row defaults to 0%, so slide it up to allocate.
- Run the simulation. The cycle engine uses your uploaded returns for that ledger.
Per-series allocation targets (advanced)
A custom series is an asset class — once you've allocated, say, 10% of your equity to a custom dividend fund, the rebalancer treats it the same as the rest of your equity sleeve. Over time market drift moves the within-class mix around: if your custom series underperforms the default sp500, its share of equities shrinks.
To pin the per-series mix, open the Asset Allocation over time editor (from the Allocation Picker → "Edit glide path"). When any asset class holds more than one series, the chart shows one line per series (e.g. S&P 500 Composite, Small-cap Growth, US 10Y Treasury) instead of one line per class. Drag any line to set its percentage-of-portfolio target; the other lines auto-renormalize so the total stays at 100% at every year. The rebalancer holds each series at its target for the full simulation horizon.
Limitations
- Up to 200 series per account, 200 years per series, 10MB per upload.
- Custom series can't be shared between users. Each upload is private.
- v1 doesn't let you splice a custom long-history proxy. Pre-history
years for short-history custom series fall back to
sp500_composite(equity) orust_10y(bond). - You must have the right to use the data. FIREproof has no view into your data's license. That responsibility is yours.
Troubleshooting
- "Code is reserved for a public series." The code you
uploaded matches a built-in series (e.g.
sp500_composite). Rename the column header and re-upload. - "NaN values." The parser found a cell that isn't a finite number. Empty cells are fine; the most common cause is text like N/A or a dash character in the data grid.
- "This .xlsx has formulas but no cached values." Re-save the spreadsheet from Excel or LibreOffice so cached calculated values are stored, then re-upload. Alternatively, export the data tab as CSV.
- Returns look off by 100x. Toggle the
Values are Percent / Decimal radio. If your file stores
0.05for 5%, pick Decimal. If it stores5.0, pick Percent.
Related
For sim-specific issues, open Plan Diagnostics from the Proof view. For everything else, reach out to support.