CoastFIRE Calculation Reality Check: Can Beancount Handle the "What If" Game We Actually Need?

CoastFIRE Calculation Reality Check: Can Beancount Handle the “What If” Game We Actually Need?

So I hit a milestone last month that I’ve been working toward for 3 years: my Beancount ledger shows I’ve crossed my CoastFIRE number. According to my calculations, I have enough invested that if I never contribute another dollar, compound interest will carry me to full FI by age 60. That should feel amazing, right?

Instead, I’m lying awake at 2am questioning every assumption. What if returns aren’t 7%? What if they’re 5%? What about inflation? What if I’m completely wrong?

The Historical vs. Projected Disconnect

Here’s my problem: Beancount is phenomenal at tracking what happened, but CoastFIRE is entirely about what will happen. My ledger gives me perfect historical accuracy—every transaction, every balance, every return is precise and auditable. But CoastFIRE requires projecting 25+ years into the future with massive assumptions:

  • Return rate assumptions: Historical 7% real returns vs. 2026’s more conservative 5-6% guidance
  • Crossover date calculations: When can I actually stop contributing?
  • Sensitivity testing: What if markets tank for 5 years? 10 years?
  • Safe withdrawal rate: Is 4% still safe, or should I plan for 3.5%?

None of this lives naturally in my Beancount ledger. I’ve got perfect historical data and… hopeful spreadsheet guesses.

My Janky Current Setup

Right now, I’m running Python scripts that:

  1. Read current balances from bean-query
  2. Apply compound interest formulas with hardcoded assumptions
  3. Spit out projected balances and “crossover dates”
  4. Save results to a text file (not version controlled :person_facepalming:)

This feels completely disconnected from my ledger. When I update my assumptions (which I’m doing constantly in 2026’s uncertain environment), there’s no audit trail. When I want to discuss my calculations with my partner or this community, I have to share separate files.

The beautiful plain-text, version-controlled philosophy of Beancount breaks down at exactly the moment when I need it most—when I’m making huge life decisions based on 25-year projections.

The 2026 Reality Check

Every financial planner is now recommending more conservative return assumptions. The old “7% real returns” rule is being revised to 5-6% for 2026 planning. That difference is MASSIVE:

  • 7% returns: I’m at CoastFIRE today, can stop contributing
  • 5% returns: I need another $85K invested, or 18 more months of aggressive saving

That’s the difference between walking into my boss’s office next month vs. grinding for another year and a half.

What I Want (But Don’t Have)

I wish I could:

  • Track multiple scenarios in a structured way (conservative/moderate/optimistic)
  • Version control my assumptions alongside my ledger (when did I switch from 7% to 5.5%?)
  • Visualize the uncertainty (confidence intervals, not single projections)
  • Share my calculations transparently with others for feedback
  • Recalculate automatically when my balances update monthly

Basically, I want Beancount’s philosophy (plain text, version controlled, scriptable) applied to forward-looking projections, not just historical transactions.

How Are You Handling This?

For those of you tracking CoastFIRE, LeanFIRE, or any FIRE variant in Beancount:

  1. Where do your projections live? Separate spreadsheets? Python scripts? Jupyter notebooks?
  2. How do you handle assumptions? Document them? Version control them?
  3. How often do you recalculate? Monthly when balances update? Quarterly? Only when markets move significantly?
  4. Have you built any tools to bridge this gap? Plugins? Scripts? Custom workflows?
  5. Am I overthinking this? Should I just accept that projections and historical data are fundamentally different things?

I know Beancount users are technical enough to script solutions, but I’m curious if anyone’s found an elegant way to keep everything in the same ecosystem. Or is the answer just “keep them separate, stupid”?

Show me your workflows. I can’t be the only one losing sleep over return rate assumptions! :bar_chart::zzz:


Context: I’m 32, software engineer salary, maxing out 401k and Roth IRA, currently at $210K invested, targeting $1.2M at age 60 (CoastFIRE number per calculators). Using Beancount + Fava for 2 years, love the historical accuracy but struggling with the projection layer.

This post really resonates with me because I went through this exact crisis about 2 years ago! The 2am anxiety spiral about return assumptions? Check. The disconnected Python scripts? Check. The feeling that Beancount’s beautiful philosophy breaks down right when you need it most? Double check.

Here’s what I learned after a lot of trial and error.

My Evolution (AKA: How I Stopped Worrying and Embraced Separation)

Phase 1: Excel Hell (2023)
Started with separate Excel projections. They got out of sync with my Beancount balances within weeks. I’d update my ledger but forget to update the spreadsheet, or vice versa. Absolute disaster.

Phase 2: Beancount Comment Overload (Early 2024)
Tried embedding projection assumptions as comments in my Beancount file. Things like:

; CoastFIRE calculation as of 2024-02-15
; Current balance: $187,450
; Assumed 7% real return
; Target: $1.1M by age 62
; Years to go: 28
; Projected result: SUCCESS!

This was… messy. Comments got stale, I had multiple versions scattered through the file, and there was no good way to visualize scenarios.

Phase 3: Separate Python Layer (Current)
Finally accepted that historical truth and future projections are fundamentally different things and should live in different places. Here’s my current workflow:

The Workflow That Actually Works

  1. Beancount ledger: Historical transactions only (single source of truth)
  2. Python projection script: Lives in the same Git repo, reads from Beancount
  3. Version control: Both files versioned together, so assumption changes have audit trail
  4. Monthly ritual: Run bean-query to get current balances → feed to projection script → generate markdown report

My script is embarrassingly simple:

# coastfire_projection.py (pseudocode)
current_balance = get_balance_from_beancount()
scenarios = {
    'conservative': 0.05,
    'moderate': 0.06,
    'optimistic': 0.07
}
for name, rate in scenarios.items():
    years_to_fi = calculate_years(current_balance, fi_target, rate)
    print(f"{name}: {years_to_fi} years until FI")

Output goes to a markdown file with a timestamp, also version controlled. So I can git log coastfire_projections/ and see exactly when I changed my assumptions and why (commit messages!).

The Key Insight That Made Me Feel Better

Separating historical from projected actually makes me trust the projections MORE, not less.

Why? Because:

  • Historical data stays pristine and auditable (crucial for tax purposes)
  • Projections are clearly labeled as “assumptions” not “facts”
  • I can update assumptions without touching my ledger
  • Version control shows the evolution of my thinking
  • I can run multiple scenarios without cluttering my books

When you mix them, you lose the clarity of “this happened” vs. “this might happen.”

My 2026 Advice

  1. Embrace separate layers: Beancount for historical, Python/Jupyter for projections
  2. Version control everything: Same repo, different directories
  3. Document assumptions in commit messages: “Switching to 5.5% return assumption based on 2026 market conditions”
  4. Run multiple scenarios: Conservative/moderate/optimistic gives you a range instead of false precision
  5. Don’t over-optimize: Your assumptions will be wrong anyway. Build for clarity, not perfection.

On Your Question About Frequency

I recalculate monthly when I update my ledger. Takes 5 minutes:

  1. Close the month in Beancount
  2. Run bean-query to extract investment balances
  3. Run projection script
  4. Review markdown output
  5. Commit both the updated ledger and new projections

If markets move dramatically (+/- 10%), I might do an ad-hoc run to see impact, but I try not to obsess.

One Warning

Don’t fall into the trap I did of building increasingly complex projection systems. I spent 3 weekends building a Monte Carlo simulation with stochastic returns and… it didn’t actually help me make better decisions. The simple three-scenario approach (5%, 6%, 7%) gives 90% of the value with 10% of the effort.

Start simple. Iterate if needed. But don’t let perfect be the enemy of good enough.


TL;DR: Separate your historical ledger (Beancount) from projections (Python script). Version control both. Run multiple scenarios monthly. Don’t overthink it. I’ll drop my script in a GitHub gist if there’s interest!

You’re not overthinking this—you’re at exactly the point where everyone hits this realization. It’s uncomfortable, but embracing separation is the answer. :bullseye:

This thread is gold! As a developer who just started with Beancount 6 months ago, this “historical truth vs. future projections” distinction really clicks for me. It’s basically version control for financial assumptions—and that’s beautiful.

The Developer’s Perspective

@helpful_veteran’s workflow feels very much like how we structure software projects:

  • Source data (Beancount ledger) = your production database
  • Business logic (Python projection scripts) = your application layer
  • Version control (Git for both) = audit trail and change management

This separation of concerns is a core software engineering principle! You don’t mix raw data with business logic for good reasons—same applies here.

Practical Questions for Setup

I’m excited to try this approach, but I have some questions about the mechanics:

  1. Git repo structure: Do you keep the projection scripts in the same repo as your ledger? Or separate repos? I’m thinking:

    my-finances/
    ├── ledger/
    │   └── 2026.beancount
    ├── projections/
    │   ├── coastfire_projection.py
    │   └── results/
    │       ├── 2026-01-projection.md
    │       └── 2026-02-projection.md
    └── README.md
    
  2. Assumption documentation: How do you document why you changed assumptions? Just in commit messages? Or do you maintain a separate assumptions.md file that gets updated?

  3. Jupyter notebooks: Anyone using these for exploratory analysis? I’m imagining loading Beancount data into pandas and playing with different scenarios interactively. Worth learning?

A Plugin Idea (Maybe Overthinking?)

Part of me wonders if we could build a Beancount scenario plugin that:

  • Reads current account balances
  • Takes parameters (return rates, years, targets)
  • Outputs projected balances with confidence intervals
  • Integrates with Fava for visualization

But then I worry: should projections be this tightly coupled to the ledger? Historical data is TRUTH—immutable, auditable, compliance-ready. Projections are GUESSES—assumption-dependent, sensitivity-tested, constantly revised.

Maybe they should live separately so we never confuse the two?

The Git Workflow I’m Imagining

# Monthly finance ritual
cd my-finances
git pull  # Get latest from remote

# Update ledger with this month's transactions
vim ledger/2026.beancount
bean-check ledger/2026.beancount  # Validate

# Run projection script
python projections/coastfire_projection.py > projections/results/2026-03-projection.md

# Review changes
git diff projections/results/

# Commit both together
git add ledger/2026.beancount projections/results/2026-03-projection.md
git commit -m "March 2026 close: Net worth +2.3%, switching to 5.5% return assumption due to market volatility"
git push

This gives you a perfect audit trail: every balance update is paired with its projection impact, and commit messages document your thinking.

One Concern: The Coupling Problem

My only hesitation is about keeping them in the same repo. In software, we separate concerns to avoid tight coupling. If projections read directly from the ledger, there’s implicit coupling. What if:

  • You refactor account names in Beancount
  • Your projection script breaks because it’s looking for old account names
  • You have to update both simultaneously

Is this a real problem in practice, or am I overthinking?

What I’m Going to Try

Based on this discussion, I’m going to:

  1. Keep my Beancount ledger pristine (historical only)
  2. Create a projections/ directory in the same repo
  3. Build a simple Python script with 3 scenarios (5%, 6%, 7%)
  4. Output to markdown with timestamps
  5. Version control everything together

And then I’ll see if the coupling becomes a problem or if it’s actually helpful to have them together.


Would love to see @helpful_veteran’s GitHub gist when it’s ready! And if anyone’s using Jupyter notebooks for this, I’d love to hear about your workflow. The developer in me wants to over-engineer this, but I’m trying to resist :sweat_smile:

As a CPA who works with both individuals and small businesses, I want to add a professional accounting perspective to this excellent discussion. The separation between historical and projected data isn’t just a technical preference—it’s actually an accounting best practice with real compliance implications.

Why CPAs Separate Historical vs. Projected

In professional accounting, we maintain strict boundaries between:

  1. Historical Financial Statements (balance sheet, income statement)

    • Based on actual transactions
    • Auditable and verifiable
    • Required for tax compliance, loan applications, investor relations
    • Must follow GAAP or other accounting standards
    • Legally significant (you can get sued for misstatements)
  2. Pro Forma Projections (forecasts, budgets, scenarios)

    • Based on assumptions and estimates
    • Clearly labeled as “forward-looking” with disclaimer language
    • Used for decision support and planning
    • No standardized format required
    • Explicitly NOT guarantees

Mixing these creates liability risk. If you present projections as facts, you’re misrepresenting your financial position. If you contaminate historical records with projected transactions, you’ve compromised your audit trail.

The Beancount Parallel

What you’re all describing (Beancount for historical, Python for projections) mirrors this professional practice perfectly:

  • Beancount ledger = auditable historical financial statements
  • Projection scripts = pro forma financial models
  • Version control = your audit documentation showing assumption changes
  • Separate but linked = best practice for transparency and risk management

This isn’t just neat—it’s the RIGHT way to structure financial data from a compliance standpoint.

Tax Planning Implications for FIRE

Here’s where this gets really important for CoastFIRE planning: your projections directly impact tax strategy decisions.

Example Scenario

You’re projecting when to do Roth conversions. This requires modeling:

  • Current income and tax bracket
  • Projected retirement income
  • Capital gains timing
  • Social Security benefit timing
  • RMD (Required Minimum Distribution) implications

These projections need documentation because your CPA (or you, if self-preparing) needs to justify tax strategy choices if audited by the IRS. Your audit trail should show:

  • What assumptions you used (documented in version control)
  • When you changed assumptions (Git commits with timestamps)
  • Why you made tax decisions based on those projections (commit messages)

Plain text + version control gives you exactly the audit trail that protects you in a tax examination. A spreadsheet with no history? Not so much.

Conservative Return Assumptions in 2026

I strongly support the shift from 7% to 5-6% real returns for 2026 planning. Here’s why from a professional standpoint:

Prudent financial planning uses conservative assumptions. If you’re wrong on the upside (returns are better than expected), you’re pleasantly surprised and retire sooner. If you’re wrong on the downside (returns are worse), you’re financially destroyed.

For tax planning specifically:

  • Lower return assumptions → plan for lower account balances → more conservative Roth conversion amounts
  • Higher return assumptions → plan for higher balances → aggressive Roth conversions that might not be necessary

Getting this wrong can cost you tens of thousands in unnecessary taxes or missed opportunities.

Practical Recommendation: Jupyter Notebooks

To answer @newbie_accountant’s question: Yes, Jupyter notebooks are worth learning for this.

Here’s why I recommend them for FIRE projections:

  1. Narrative + Code: You can explain your assumptions in markdown cells, then show the calculation in code cells
  2. Scenario Comparison: Easy to visualize multiple scenarios side-by-side with charts
  3. Reproducible: Save the notebook, and you have both the logic and the output documented together
  4. Shareable: Send to your CPA, financial advisor, or partner for review
  5. Tax Documentation: Export to PDF for your tax files

A typical workflow:

# Load balances from Beancount
import subprocess
balance_query = "SELECT account, sum(position) WHERE account ~ 'Assets:Investments'"
result = subprocess.run(['bean-query', 'my_ledger.beancount', balance_query], capture_output=True)

# Parse and project
current_balance = parse_bean_query_output(result.stdout)
scenarios = {
    'conservative': calculate_coastfire(current_balance, 0.05, 28),
    'moderate': calculate_coastfire(current_balance, 0.06, 28),
    'optimistic': calculate_coastfire(current_balance, 0.07, 28)
}

# Visualize
import matplotlib.pyplot as plt
plot_scenarios(scenarios)

Then you have a dated notebook file that documents exactly what you calculated when, with what assumptions, and what the results were.

Repository Structure Recommendation

Based on how I structure client files, here’s what I recommend:

my-finances/
├── ledger/
│   ├── 2024.beancount
│   ├── 2025.beancount
│   └── 2026.beancount
├── projections/
│   ├── assumptions/
│   │   └── 2026-assumptions.md  # Document assumption changes
│   ├── scripts/
│   │   └── coastfire_projection.py
│   └── results/
│       ├── 2026-01-projection.ipynb
│       ├── 2026-02-projection.ipynb
│       └── 2026-03-projection.ipynb
├── tax/
│   └── 2026-tax-planning.ipynb  # Roth conversion modeling
└── README.md

Key points:

  • Separate assumptions documentation: When you change return assumptions, document WHY in assumptions/
  • Dated notebook results: Each month’s projection is a separate file, preserving history
  • Tax planning separate: Projections that affect tax decisions get their own space

On the “Coupling Problem”

@newbie_accountant asked about tight coupling between ledger and projection scripts. In practice, this is rarely a problem if you follow one rule:

Query by account type, not specific account names.

Instead of:

balance = query("SELECT sum(position) WHERE account = 'Assets:Investments:Vanguard:VTSAX'")

Do:

balance = query("SELECT sum(position) WHERE account ~ 'Assets:Investments'")

This way, you can rename accounts in your ledger without breaking projections. The coupling is at the account type level (which is stable) not the specific account name level (which changes).

Final Thought: This Is What Makes Plain Text Powerful

The fact that we’re having this discussion—about version control, audit trails, separation of concerns, reproducible analysis—shows why plain text accounting is revolutionary for personal finance.

Commercial software doesn’t give you this. You get whatever reports they built, with whatever assumptions they hardcoded, with no audit trail of what changed when.

With Beancount + Python + Git, you’re building your own financial infrastructure with professional-grade transparency and auditability. That’s worth the learning curve.


P.S.: If anyone wants help setting up Jupyter notebooks for FIRE projections, I’m happy to share a starter template. I’ve got clients who use this exact workflow and it works beautifully for year-end tax planning discussions.