Skip to content

Notes from the Group

simonmichael edited this page Jun 21, 2011 · 7 revisions

The following notes are extracted from the mailing list and seek to provide some insight until the documentation for 3.0 is written.

Commodity Names

May contain numbers or spaces but must be quoted in that case.


If a posting comment is a date (with brackets), it modifies the date for that posting:

2010/02/01 Sample
  Assets:Bank  $400.00
  Income:Check  $-400.00 ; [2010/01/01]

You can use metadata to override the payee field for individual postings within a transaction: (source)

2010/06/17 Sample
  Assets:Bank  $400.00
  Income:Check1  $-100.00 ; Payee: Person One
  Income:Check2  $-100.00 ; Payee: Person Two
  Income:Check3  $-100.00 ; Payee: Person Three
  Income:Check4  $-100.00 ; Payee: Person Four

Metadata are normally strings, but you can create metadata of other types:

2010/06/17 Sample
  Assets:Bank  $400.00
  Income:Check1  $-100.00
  ; Date:: [2010/09/01]
  ; Amount:: $100.00

(Note that this Date tag is not the same as the posting date.)

There are now tag/pop directives, to apply metadata to a range of transactions (and their postings). For example, if you wanted a conceptual "page" of transactions relating to business trip to Chicago, you could do this:

 tag Location: Chicago
 tag Purpose: Business

 ... transactions go here


It would be as if you'd applied "; Location: Chicago", etc., to every transaction.


'tag("Date") < [2011]' (for typed Date tag)

'commodity(tag("Amount")) == "$"'

Now the following is supported:


Where COMMAND is any command verb, OPTIONS can occur anywhere, and SEARCH-TERM is one or more of the following:

 word              search for any account containing 'word'
 TERM and TERM     boolean AND between terms
 TERM or TERM      boolean OR between terms
 not TERM          invert the meaning of the term
 payee word        search for any payee containing 'word'
 @word             shorthand for 'payee word'
 desc word         alternate for 'payee word'
 note word         search for any note containing 'word'
 &word             shorthand for 'note word'
 tag word          search for any metadata tag containing 'word'
 tag word=value    search for any metadata tag containing 'word'
                   whose value contains 'value'
 %word             shorthand for 'tag word'
 %word=value       shorthand for 'tag word=value'
 meta word         alternate for 'tag word'
 meta word=value   alternate for 'tag word=value'
 expr 'EXPR'       apply the given value expression as a predicate
 '=EXPR'           shorthand for 'expr EXPR'
 \ ( TERMS \ )     group terms; useful if using and/or/not

(That last one should have no space between backslash and parenthesis. The obvious ways to type it in Markdown aren't working.)

So, the example given at the top of this e-mail could now be:

 ledger reg food not dining @chang
 ledger reg food and not dining and not payee chang
 ledger reg food not dining expr 'payee =~ /chang/'

All three are equivalent.

Checks and Assertions

Use "assert" instead of "check" if you want it to be an error.

These can occur in many places: (source)

; Within an automated transaction, the assert is evaluated every time
; a posting is matched, with the expression context set to the
; matched posting.
= Food
  assert account("Expenses:Food").total >= $100

= expr account =~ /Food/
  assert account("Expenses:Food").total >= $100

2010-06-12 Sample
  Expenses:Food                $100

; At file scope, the expression is evaluated within "global" scope.
assert account("Expenses:Food").total == $100

; At the top of a transction, the assertion's scope is the
; transaction.  After a posting, the scope is that posting.  Note
; however that account totals are only adjusted after successful
; parsing of a transaction, which means that all the assertions below
; are true, even though it appears as though the first posting should
; affect the total immediately, which is not the case.
2010-06-12 Sample 2
  assert account("Expenses:Food").total == $100
  Expenses:Food                $50
  assert account("Expenses:Food").total == $100
  assert account("Expenses:Food").total == $100


impose sanity limits on transaction amounts, e.g. flag anything £5000 (source)

= true
    check abs(amount) < 5000

Declare pairs of accounts between which transfers could validly occur (source)

= /Checking/
    check account =~ /Expense/
2010-06-24 Sample
    Expenses:Food    00

(Note however that this currently does not work as /Checking/ matches the Assets:Checking posting and thus the value of account won't be 'Expenses:Food' nor is there any way to get to that other posting if I'm not mistaken.)



In the next branch there is a new option: --group=by=EXPR. It causes multiple sub-reports to be generated, one for each varying value of EXPR. In cases where EXPR returns a null, no report is created. For example, you can see multiple register reports for the differing values of the tag Client -- and only for transactions which have that tag -- using:

ledger reg --group-by='tag("Client")'

Or, see which accounts were involved in transactions with what other accounts:

ledger reg --group-by=account --related --subtotal

In some ways, the use of --group-by=payee could be emulated with a shell script, for example:

ledger reg -F '%(payee)\n' | sort | \
while read payee ; do \
  echo ; echo $payee ; ledger reg payee "/$payee/"; \

However, the --group-by option has two main advantages over such methods:

  1. Your data file is only parsed and filtered once, rather than N times.

  2. Ledger knows to ignore null values (which are different from empty strings and zeroes, something the -F flag can't tell you).

You can use the option --group-title-format to apply underscores to the group titles, for example:

--group-title-format='%(value)\n%("-" * 79)\n'

Or you can omit group titles altogether with --no-titles. Sometimes, the titling can be redundant, while most times it is necessary to make sense of what you're seeing. Note that the print report never shows titles, as the objective of print is to always render output that can be parsed again as input.


NOTE: the order of budget entries is important. The following budget

~ Monthly
    Expenses         $200.00
    Expenses:Food    $100.00
    Expenses:Health   $20.00

is invalid! Any Expenses:Food or Expenses:Health posting will match the first Expenses. The proper budget looks like this:

~ Monthly
    Expenses:Health   $20.00
    Expenses:Food    $100.00
    Expenses         $200.00

To sort budget elements always use reversed order (sort -r), this assures that child accounts always come before their parents (e.g. Expenses:Food before Expenses).

Further reading