Balances and Padding
A balance assertion states what an account held at a moment, as your bank statement or your wallet says. Zhang checks it against the postings of the account. Asserting balances regularly catches typos, forgotten and duplicated transactions while they are still easy to find.
Padding is the other half: it fills in an amount you cannot or do not want to account for in detail, such as the money an account held before you started your ledger.
The exact syntax of both is in Balance, and the details of padding in Padding with with pad and The pad directive.
Assert a balance
Section titled “Assert a balance”Your bank statement for January ends with a balance of 16,643.60 CNY:
2024-01-01 open Assets:Bank:Checking CNY2024-01-01 open Income:Salary CNY2024-01-01 open Expenses:Food CNY2024-01-01 open Equity:Opening-Balances CNY
; the 5,000 CNY the account held before the ledger starts, see below2024-01-01 balance Assets:Bank:Checking 5000 CNY with pad Equity:Opening-Balances
2024-01-05 * "ACME Corp" "January salary" Assets:Bank:Checking 12000 CNY Income:Salary
2024-01-20 * "Supermarket" Assets:Bank:Checking -356.40 CNY Expenses:Food
2024-02-01 balance Assets:Bank:Checking 16643.60 CNY- An assertion is checked at the start of its date: it counts everything dated before it and nothing dated that day. To check a statement that ends on 31 January, date the assertion 1 February.
- With a time of day,
2024-01-31 23:59:59 balance …, it counts the entries dated earlier that day too. An entry without a time counts as00:00:00, and an assertion comes before any other entry with the same date and time. In a beancount ledger the time of abalanceis ignored, as beancount ignores it: the assertion is checked at the start of its date, and aBalanceTimeIgnorednotice tells you when that changes what it checks. - The balance of an account includes its sub-accounts:
balance Assets:Bank …checksAssets:Bank,Assets:Bank:Checkingand every other account underAssets:Banktogether. - An assertion checks one commodity. Write one line per commodity for an account that holds several. A commodity the account does not hold counts as zero.
Exact, unless you allow a tolerance
Section titled “Exact, unless you allow a tolerance”An assertion holds only when the balance equals the amount exactly. 16643.604 CNY does not hold for a balance of 16643.60 CNY, and neither does 16643.6 for 16643.604.
When your source rounds, for example an app that shows a fund to two decimals while the units have more, write the tolerance you accept after ~:
2024-02-01 balance Assets:Bank:Checking 16643.60 ~ 0.01 CNYIt holds when the balance is within 0.01 CNY of 16,643.60 CNY. Zhang never derives a tolerance from the number of decimals you write, and no option loosens assertions: default_balance_tolerance_precision, despite its name, does not.
Start from an existing balance
Section titled “Start from an existing balance”Your bank account already held money when you started the ledger. Instead of recording its whole history, pad it to the balance it had:
2024-01-01 open Assets:Bank:Checking CNY2024-01-01 open Equity:Opening-Balances CNY
2024-01-01 balance Assets:Bank:Checking 5000 CNY with pad Equity:Opening-Balanceswith pad Equity:Opening-Balances asks Zhang to add a transaction that brings the account to 5,000 CNY, taking the difference from Equity:Opening-Balances:
- The padding transaction has the flag
P, the payeeBalance Padand the narrationpad Assets:Bank:Checking to Equity:Opening-Balances. It is dated on the assertion’s date and comes before that day’s other entries, so the account holds 5,000 CNY at the start of the day. The Journals page lists it as a Pad. - It is sized from the balance the postings give at that point. If the account already holds the amount, Zhang adds nothing.
- The assertion is then checked like any other, against the padded balance.
- Padding is not limited to opening balances. It also closes a gap you will never reconstruct, such as a wallet after a month of small cash spending:
2024-01-01 balance Assets:Cash 200 CNY with pad Equity:Opening-Balances
2024-01-10 * "Bakery" Assets:Cash -18 CNY Expenses:Food
; counted 150 CNY: the other 32 CNY went on things not worth recording2024-02-01 balance Assets:Cash 150 CNY with pad Expenses:MiscHere Zhang moves 32 CNY from Assets:Cash to Expenses:Misc on 1 February.
The pad directive
Section titled “The pad directive”A pad and the assertion it serves can also be two directives, as in beancount. This works in Zhang files and beancount files alike:
2024-01-01 pad Assets:Bank:Checking Equity:Opening-Balances2024-01-02 balance Assets:Bank:Checking 1000.00 USDZhang pairs them the way beancount does: a pad serves the next balance of its account in each commodity on a later day, up to the account’s next pad. The two may be in different files. A balance on the same day as the pad is not padded.
- The padding transaction is dated on the
pad, here 1 January, as beancount dates it, so the account holds the padded balance from that day on. - A
padthat nobalanceneeds is reported asUnusedPad, as beancount reports it. - Padding a commodity the account holds at cost, such as shares bought with a cost, is reported as
PadWithCost: book those units with their cost instead.
When an assertion fails
Section titled “When an assertion fails”A failing assertion changes nothing in your books: the account keeps the balance its postings give, and later assertions and pads are measured from that balance too. Zhang only reports it:
- The ledger’s error list on the Overview page shows an
AccountBalanceCheckErrorwith the name of the account. - On the Journals page the assertion is marked Check failed. Its preview shows the asserted balance, the accumulated balance, the difference and the tolerance.
- On the account’s page, the Journals tab shows the assertion as a row with the amount it asserted next to the running balance.
To find the cause:
-
Look at the difference. It often equals one transaction: a forgotten one, one recorded twice, or one with the wrong sign (twice its amount).
-
List the postings of the account with their running balance and compare them with the statement, line by line:
JOURNAL 'Assets:Bank:Checking' FROM date >= 2024-01-01 AND date < 2024-02-01 -
Narrow the period down with more assertions, for example one per statement or per week. The first one that fails tells you where to look.
-
List every failing assertion with the difference, the balance the postings give minus the asserted amount, on the Query page:
SELECT date, account, amount, discrepancy FROM #balances WHERE discrepancy IS NOT NULL
When you have found and fixed the transactions, the assertion holds again. If you decide the difference is not worth finding, turn the assertion into balance … with pad to an expense account, which records the difference on purpose.
Assert balances in the web UI
Section titled “Assert balances in the web UI”- On an account’s page, the Balance check tab lists the commodities the account holds. Enter the actual balance of one and select Check balance. To pad the difference, pick an account under Pad from and select Pad & check.
- Tools → Batch balance does the same for many accounts at once. Rows left empty are skipped.
The web UI writes them to the file the directive_output_path option names:
- In a Zhang ledger, it writes
balanceorbalance … with paddated with the current date and time, so they count everything recorded up to now, today’s entries included. - In a beancount ledger, which knows no times, “my balance now” is a
balancedated tomorrow, after every entry of today. A pad writes the difference as a padding transaction dated now, not apaddirective. Checking the same account and commodity again the same day replaces that balance in place, without its~tolerance, and the web UI lists the balances it replaced.
Zhang refuses, and writes nothing for, a balance it could only report as an error once written: an account that is not open, a pad from the account itself or one of its sub-accounts, a pad of a commodity held at cost, or, in a beancount ledger, a balance that a pad you wrote would serve. The message tells you why and what to change. See From the web UI.
Plugins, pads and assertions
Section titled “Plugins, pads and assertions”Zhang processes a ledger in this order: first the plugins, in the order they are declared, then the check that every account is open, then the pads, then the assertions. So a transaction a plugin adds is part of the balance a pad fills up to and an assertion checks. A plugin sees your balance … with pad directives, but not the padding transactions, which do not exist yet when it runs. A plugin does not see pad directives either: a balance a pad serves is shown to it as a balance … with pad. See Writing plugins.
