Skip to content

Financial Targets Page

Route: /targets (targets.html) Nav label: Targets Last Updated: 2026-08-11


What It Does

Tracks progress toward five long-term financial goals and projects how long each takes to reach under different monthly contributions. Distinct from the Budget page, which is monthly category spend limits.

Targets are not user-editable. There is no create-a-goal UI and no targets table. The five goals and their dollar amounts are constants in api_targets_summary() in app.py. Changing a target means editing the code and redeploying.

Current values are not manually maintained either. Every goal reads the latest account_balances row per active account, joined through accounts.display_group and accounts.account_type. Recording a balance or uploading a statement moves the numbers.


The Five Targets

# Target Amount Which accounts
1 Emergency Fund $100,000 display_group = 'cash' AND account_type IN ('savings', 'money_market')
2 529 Plans $100,000 each account_type = '529', one card per plan
3 Investments $2,500,000 account_type = 'brokerage'
4 Mortgage Payoff payoff, not a dollar goal Latest mortgage_statements row
5 Retirement / FIRE see below display_group = 'retirement'

An account with is_active = false is excluded from all of them.

Retirement targets

Per-type, and scaled by how many accounts of that type exist — two Roth IRAs give a $1M target, not $500k.

Account type Target per account
roth_ira $500,000
ira $500,000
401k $1,500,000

The FIRE number

fire_target = annual_spend × 25 (the 4% rule), where annual_spend is the last 12 months of bank_transactions excluding Income, Transfer, Transfers, Mortgage, Retirement Funds, and Stock Funds.

annual_spend already excludes Mortgage

An earlier version subtracted the annualized mortgage a second time, producing a $168k FIRE target. Fixed 2026-07-08. Do not re-subtract.

The retirement projection aims at whichever unmet goal is higher: the FIRE number while retirement savings are below it, the summed account targets once past it.


Projections

Three of the five targets project forward with a 7% assumed annual return, compounded monthly, over a 600-month horizon. Each returns a chart sampled quarterly plus the month the goal is reached.

Target Monthly contribution scenarios
Investments $1,000 / $3,000 / $5,000
Retirement $500 / $1,000 / $2,000
Mortgage Minimum only, +$500, +$1,000, +$2,500, plus a computed "pay off in 15 years" and "pay off in 10 years" extra

Mortgage amortization is real, not a projection: _amortize() runs the schedule with escrow held separate, and each scenario reports payoff date, total interest, and interest saved versus the minimum-payment baseline.

When mortgage_statements is empty, mortgage.has_data is false and the section is skipped. When min_payment is missing from the statement it is derived: principal + interest + escrow, and failing that, a standard 360-month payment from balance and rate.


API

GET /api/targets-summary returns all five in one call.

{
  "emergency":  { "current": 0, "target": 100000, "pct": 0, "remaining": 0, "accounts": [] },
  "plans_529":  [ { "name": "", "current": 0, "target": 100000, "pct": 0, "as_of_date": "" } ],
  "investment": { "current": 0, "target": 2500000, "pct": 0, "scenarios": [] },
  "mortgage":   { "has_data": true, "balance": 0, "rate": 0, "min_payment": 0, "escrow": 0,
                  "statement_date": "", "scenarios": [], "history": [] },
  "retirement": { "current": 0, "target": 0, "by_type": {}, "fire_target": 0,
                  "annual_spend": 0, "fire_pct": 0, "assumed_annual_return": 0.07,
                  "scenarios": [] }
}

On error it returns 500 with both error and a full trace.


Notes

  • Balances are as stale as the last statement or manual entry. Each account carries its as_of_date
  • Adding a target, or changing an amount, is a code change in api_targets_summary()
  • Investment and retirement projections assume a flat 7% and a constant contribution. No inflation, no tax, no sequence-of-returns modelling