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