Skip to content

Budgeting

This is the reference for how budgeting works in Finzytrack. It describes the directive that stores a budget, the semantics Finzytrack applies when resolving it, and the building blocks which make up budget dashboards. For setting and tracking budgets in the app, see the Budgets view guide; for the dashboard recipe format, see Dashboard & Widget Recipes.

A budget is stored directly in your ledger as a standard Beancount custom "budget" directive — so budgets travel with your data (this is the same format used by Fava):

2026-01-01 custom "budget" Expenses:Food "monthly" 500 USD
  • date — the effective date; the budget applies from this date forward.
  • account — the budgeted account. A budget on a parent covers the parent and all descendants (see Inheritance).
  • interval — one of daily, weekly, monthly, quarterly, yearly (plus none, below).
  • amount + currency — the budget for one interval, in one currency.

You can write these by hand or let the Budgets view read and write them for you.

To stop budgeting an account/currency from a date — without deleting the earlier record — use the none interval:

2026-07-01 custom "budget" Expenses:Food "none" 0 USD

From that date the account is no longer budgeted (it drops out of budget-vs-actual tracking); the earlier directives still govern the periods before it. The 0 amount is inert — it only names the currency. This end marker is specific to Finzytrack; other Beancount tools that don’t recognize none simply ignore it and show the previous budget as continuing.

A directive applies from its date until a later directive for the same account and currency supersedes it. To change an amount mid-year, add a second directive with a later date; the earlier one still governs the earlier months.

A budget on a parent account is compared against spending on that account and all of its descendants. If you also budget a child, the child is tracked as its own line and still counts toward the parent — they overlap by design. Aggregate views use a maximal-named-subtree rule so nested budgets never double-count a total.

To set one total for a whole area, budget a grouping account (a non-leaf, e.g. Expenses:Insurance). Beancount won’t accept a bare root token (Expenses), but quoting it works and both Beancount and Fava read it:

2026-01-01 custom "budget" "Expenses" "monthly" 9000 USD

Such root/total budgets are excluded from bottom-up per-account views by default (they’d double-count against the accounts beneath them). The top-down Budget: Zero-based dashboard opts them in to carve the total into named budgets + an Unbudgeted remainder.

A budget applies only to its own currency; spending in other currencies is ignored for it. The same account can carry a separate budget per currency, tracked side by side. The unit of a budget is an (account, currency) pair.

Beancount doesn’t enforce uniqueness on custom directives. If two directives share the same (date, account, currency) with different amounts, Finzytrack takes the last (sorted by date, then source file and line) and surfaces a warning so you can clean it up — it never silently averages or drops one.

A budget can use any interval. For a calendar-aligned period with a single active budget, the period total is exactly the amount you set. For arbitrary ranges, Finzytrack uses the same full-precision daily-equivalent normalization Fava does (monthly ÷ days-in-that-month, etc.), computed per day and summed — so a range that crosses a budget change is a piecewise sum, and any date range is computable. Cross-interval views (a yearly budget summed over one month) are inherently fractional; only the display rounds.

Budget dashboards are ordinary dashboard recipes: a query step for actual spending + a compute step for the budget numbers + a transform that merges them. You can freely write your own queries, while the compute and transform functions are selected from a fixed catalog. Please raise a feature request (issue) on GitHub if you need a particular compute or transform function.

The single source of budget math. It reads your custom "budget" directives and resolves them over a date range at full precision.

  • Signature: budget_for_range(from?, to, currency?, account?, groupBy?, includeRoots?).
  • from optional — omit it to start each account at its own first budget (inception), the natural “from the beginning” for envelope balances.
  • account optional — omitted returns every budgeted account in range; given, just that one.
  • groupBy: "period" — returns a per-calendar-month series (for rollover / trends) instead of one total per account.
  • includeRoots: true — include quoted root/total budgets (the zero-based view uses this).
  • Returns [{ account, currency, budget }], or [{ account, currency, period, budget }] in period mode.

Client-side transforms that turn budgets + actuals into what a widget renders:

TransformDoes
joinBudgetActualPer-account variance { budget, actual, remaining, pctUsed, direction }. Remainder mode (config.totalAccount) adds synthetic Unbudgeted + Total rows for catch-all/zero-based, with an over-allocation flag.
joinBudgetActualByPeriodPer-(account, period) variance — feeds the adherence heat-map.
joinByPeriodMerges per-period budgets and actuals into one row per period, with the month’s date bounds so a chart series can click through to that period.
runningSumCumulative columns over a period series — burn-down / pace.
envelopeRolloverPer-period { available, carryover, overspent } — stateless envelope rollover.
envelopeBalancesEvery budgeted account’s inception-aware carried-forward balance.
budgetTreeHierarchical carve-out of a total into named budgets + remainder (the zero-based sunburst).
budgetSummaryOne aggregate row (budget / spent / remaining) for headline KPIs.
unbudgetedSpendingSpending in accounts covered by no budget — the “leak” list.

Budget-progress bars and the adherence heat-map color themselves from the theme’s favorability scale (green under → amber approaching → on-budget → red over); see Colors.

Every style is the same machinery — which numbers you enter and which pieces you compose. For choosing and tweaking a style in the app, see the Budgets guide; this is the format-side map. Finzytrack seeds one dashboard for each style that earns a dedicated one:

StyleDashboard
Per-account, no rollover (also 50/30/20, pay-yourself-first)Budget: Overview
Envelope with rolloverBudget: Envelopes
Zero-based / total-with-carve-outsBudget: Zero-based
Month-by-month history + adherenceBudget: History

50/30/20 and pace / burn-down don’t get their own dashboard — they’re quick recipes over the same blocks (Overview with group budgets; a cumulative line for one account). The Budgets guide walks through building them.