Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Accounts

An account is a named balance that the simulator tracks over time. Every account that appears in an entry or assertion must be declared.

Syntax

account <path> [= <expression> @ <date>]

The path is one or more identifiers joined by colons:

account Assets:Cash
account Assets:Retirement:Jim
account Liabilities:Loan
account Income:Gross:Salary:Jim
account Expenses:Rent

Opening balance

Without = … @ …, an account starts at zero and is available for the entire simulation. Supply an initial value and a date to give the account a known opening balance on a specific day:

account Assets:Cash             = 12_500    @ 2025-01-01
account Liabilities:Loan        = -450_000  @ 2025-01-01
account Assets:Retirement:Seb   = 87_340.22 @ 2024-07-01

The = value and @ date must always appear together — one without the other is a parse error.

The expression is evaluated on the opening date. It can reference params and accounts that are already open, but not accounts that open later or leg aggregations.

Referencing accounts before their opening date

Referencing an account (in an entry posting or an expression) before its @ date is a runtime error:

account Assets:Cash = 1000 @ 2025-06-01

// This entry fires in January — before Assets:Cash opens — and will error:
entry monthly "Paycheck" {
  Assets:Cash = 500
  Income:Salary
}

To avoid the error, ensure entry schedules start no earlier than the latest opening date of any account they reference, or set the opening date early enough to cover the simulation range.

Simulation start after opening date

If your simulation start is later than an account’s opening date, saldo automatically warms up the simulation from the opening date. All entries that fire during the warm-up period are processed normally — only their output (ledger transactions, CSV rows) is suppressed. The opening-balances entry in ledger output reflects the actual balance at your simulation start, after the warm-up.

account Assets:Cash = 1000 @ 2024-01-01

entry monthly "Paycheck" {
  Assets:Cash = 500
  Income:Salary
}

// Running from 2025-01-01: Assets:Cash opens at 1000 on 2024-01-01, then
// 12 monthly paycheck entries fire during warm-up, so the opening balance
// shown in the ledger output is 7000 (1000 + 12 × 500).

Naming conventions

saldo does not enforce any particular hierarchy, but the double-entry conventions used throughout these docs are:

RootHolds
Assets:…Things you own (cash, retirement, savings)
Liabilities:…Things you owe (loans, accrued interest)
Income:…Sources of income — carried as negative by convention
Expenses:…Spending categories

Income accounts are negative because every paycheck entry credits income (negative posting) and debits assets (positive posting), keeping the net of all postings at zero.

Using accounts in expressions

Reference an account by its full path in any expression:

// Current balance of a liability account
Liabilities:Loan * interest_rate / 365

// Assert cash never goes below a threshold
assert that Assets:Cash >= 10_000

// Compute daily interest on the outstanding balance
entry daily "Interest accrual" {
  Liabilities:AccruedInterest = Liabilities:Loan * interest_rate / 365
  Expenses:Interest
}

An account reference in an entry reads the current balance. Entries that fire on the same day run in declaration order, so a later entry sees the postings of earlier ones. Within a single entry, every posting is evaluated against the balances from before that entry fired, so one posting never sees another posting from the same entry.

Params are evaluated at the start of each day, so a param that reads an account sees the balance before any entries fire that day.

Declaration order

Accounts appear in the CSV output columns in the order they are declared. Declare them in the order you want to read them.

Complete example

account Assets:Cash             = 12_500   @ 2025-01-01
account Assets:Retirement:Jim   = 45_000   @ 2025-01-01
account Liabilities:Loan        = -320_000 @ 2025-01-01
account Income:Gross:Salary:Jim
account Expenses:Rent
account Expenses:Interest

param jim_salary     = 130_000 per year
param interest_rate  = 6.5% per year

entry monthly "Jim's paycheck" {
  Assets:Cash           = jim_salary
  Income:Gross:Salary:Jim
}

entry monthly "Rent" {
  Expenses:Rent = 3_915
  Assets:Cash
}

entry daily "Loan interest" {
  Liabilities:Loan = Liabilities:Loan * interest_rate
  Expenses:Interest
}

assert that Assets:Cash >= 0