Beancount.Renderer turns a stream of directive structs into valid Beancount text. Rendering is pure and deterministic: rendering the same stream twice produces byte-identical output. This is the foundation of golden-file testing and of using Beancount as an oracle.

The directive protocol

Every directive struct implements the Beancount.Directive protocol, which defines a single function:

@spec to_bean(t()) :: iodata()

Beancount.Renderer.render/1 calls to_bean/1 on each directive and joins the fragments with a blank line, terminating the document with a single newline.

iex> Beancount.render([Beancount.open(~D[2026-01-01], "Assets:Bank", ["USD"])])
"2026-01-01 open Assets:Bank USD\n"

What is supported

  • dates - rendered as ISO-8601 YYYY-MM-DD.
  • flags - transaction flags (*, !) and per-posting flags.
  • postings - amounts right-aligned for readability.
  • metadata - key: value lines, emitted in sorted order for determinism.
  • tags & links - #tag and ^link suffixes, sorted.
  • commodities - currency codes on postings, balances and prices.
  • quoted strings - payees, narrations, notes, documents, events, and string metadata, with " and \ escaped.
  • cost & price annotations - {10.00 USD} cost basis and @/@@ prices.

Posting alignment

Within a transaction, amounts are right-aligned so decimal values line up:

2026-01-31 * "Employer" "Salary"
  Assets:Bank     5000 USD
  Income:Salary  -5000 USD

Alignment is computed per transaction and is fully deterministic.

Determinism guarantees

  • Metadata keys are sorted.
  • Tags and links are sorted.
  • Decimals are rendered in plain (non-scientific) notation.

These rules mean output never depends on map ordering or runtime state, so it is safe to commit rendered output as golden fixtures.