Posting Structure¶
Overview¶
A posting is a single line within a transaction that specifies a transfer of value to or from an account. Every transaction consists of two or more postings.
Syntax¶
posting = INDENT [flag] account [WHITESPACE amount [cost] [price]]
flag = "*" | "!"
cost = "{" cost_spec "}" | "{{" cost_spec "}}"
price = "@" amount | "@@" amount
Components¶
Indentation¶
Postings MUST be indented (at least one space or tab):
2024-01-15 * "Transaction"
Assets:Checking 100 USD ; Correct: indented
Income:Salary ; Correct: indented
2024-01-15 * "Invalid"
Assets:Checking 100 USD ; ERROR: not indented
Flag (Optional)¶
A posting may have its own flag independent of the transaction:
| Flag | Meaning |
|---|---|
* |
Complete/verified |
! |
Incomplete/needs attention |
2024-01-15 * "Mixed status"
Assets:Checking -100 USD ; Inherits transaction flag (*)
* Expenses:Food 50 USD ; Explicitly complete
! Expenses:Other 50 USD ; Explicitly incomplete
Account¶
The account receiving or giving value. Must be a valid account name:
2024-01-15 * "Transfer"
Assets:Checking -500 USD
Assets:Savings 500 USD
Amount (Optional)¶
A number followed by a currency:
2024-01-15 * "Purchase"
Assets:Checking -85.50 USD
Expenses:Food 85.50 USD
When omitted, the amount is computed to balance the transaction:
2024-01-15 * "Purchase"
Assets:Checking -85.50 USD
Expenses:Food ; Computed as 85.50 USD
Cost Specification (Optional)¶
Records the acquisition cost of commodities:
2024-01-15 * "Buy stock"
Assets:Brokerage 10 AAPL {150 USD} ; Per-unit cost
Assets:Brokerage 10 AAPL {{1500 USD}} ; Total cost
Assets:Cash -1500 USD
See costs.md for full cost specification documentation.
Price Annotation (Optional)¶
Records the exchange rate for currency conversion:
2024-01-15 * "Exchange"
Assets:EUR 100 EUR @ 1.10 USD ; Per-unit price
Assets:EUR 100 EUR @@ 110 USD ; Total price
Assets:USD -110 USD
See prices.md for full price annotation documentation.
Amount Elision¶
Rules¶
UNDEFINED: The exact elision rule is pending clarification. See: Pending Issue - Amount Elision Rule
Option A (One per currency): At most one posting per currency MAY omit its amount. Multiple currencies may each have one elided posting.
Option B (One total): At most one posting total MAY omit its amount. That posting expands to cover all currencies.
Regardless of which rule applies: - The omitted amount(s) are computed to make the transaction balance - The sum of all posting weights for each currency MUST equal zero
Examples¶
; Single currency - unambiguous
2024-01-15 * "Simple"
Assets:Checking 100 USD
Income:Salary ; Computed as -100 USD
Invalid Examples¶
; ERROR: Two elided postings for same currency
2024-01-15 * "Ambiguous"
Assets:Checking 100 USD
Expenses:Food
Expenses:Coffee ; Which gets how much?
; ERROR: Cannot compute without other amounts
2024-01-15 * "Empty"
Assets:Checking
Income:Salary ; No amounts to compute from
Weight Calculation¶
The posting weight determines how it contributes to transaction balancing:
| Posting Type | Weight Formula |
|---|---|
| Simple amount | amount |
With cost {cost} |
units × cost |
With total cost {{cost}} |
cost |
With price @ price |
units × price |
With total price @@ price |
price |
| Cost + Price | units × cost (price is informational) |
Examples¶
; Simple: weight = 100 USD
Assets:Checking 100 USD
; Cost: weight = 10 × 150 = 1500 USD
Assets:Stock 10 AAPL {150 USD}
; Total cost: weight = 1500 USD
Assets:Stock 10 AAPL {{1500 USD}}
; Price: weight = 100 × 1.10 = 110 USD
Assets:EUR 100 EUR @ 1.10 USD
; Total price: weight = 110 USD
Assets:EUR 100 EUR @@ 110 USD
; Cost + Price: weight = 10 × 150 = 1500 USD (price ignored for balance)
Assets:Stock 10 AAPL {150 USD} @ 180 USD
Posting Metadata¶
Metadata can be attached to individual postings with double indentation:
2024-01-15 * "Purchase"
transaction-meta: "value" ; Transaction metadata (single indent)
Assets:Checking -85.50 USD
bank-ref: "TXN123" ; Posting metadata (double indent)
category: "groceries"
Expenses:Food 85.50 USD
receipt: "scan.pdf"
Arithmetic Expressions¶
Amounts support arithmetic expressions:
2024-01-15 * "Split dinner"
Assets:Checking -75.00 USD
Expenses:Food:Mine (75.00 / 3) USD
Expenses:Food:Alice (75.00 / 3) USD
Expenses:Food:Bob (75.00 / 3) USD
Supported operators:
- Addition: +
- Subtraction: -
- Multiplication: *
- Division: /
- Parentheses: ( )
Balance Verification¶
After parsing all postings:
- Compute weight of each posting
- Sum weights by currency
- Each currency's sum must equal zero (within tolerance)
2024-01-15 * "Balanced"
Assets:Checking -100.00 USD ; -100 USD
Expenses:Food 50.00 USD ; +50 USD
Expenses:Coffee 50.00 USD ; +50 USD
; Sum: -100 + 50 + 50 = 0 ✓
Posting Order¶
Posting order within a transaction: - Is preserved for display purposes - Does NOT affect validation - Does NOT affect booking (unless explicitly configured)
Examples¶
Basic Transfer¶
2024-01-15 * "ATM Withdrawal"
Assets:Cash 200.00 USD
Assets:Checking -200.00 USD
Purchase with Elision¶
2024-01-15 * "Grocery shopping"
Expenses:Food 85.50 USD
Assets:Checking ; -85.50 USD computed
Stock Purchase with Cost¶
2024-01-15 * "Buy Apple stock"
Assets:Brokerage 10 AAPL {185.50 USD}
Expenses:Commission 9.99 USD
Assets:Cash ; -1864.99 USD computed
Currency Exchange with Price¶
2024-01-15 * "EUR to USD"
Assets:EUR -100 EUR @ 1.10 USD ; Gives 100 EUR
Assets:USD 110 USD ; Gets 110 USD
Complex Transaction¶
2024-01-15 * "Stock sale with commission"
Assets:Brokerage -10 AAPL {150 USD} @ 185 USD
lot-id: "2023-buy"
Assets:Cash 1840.01 USD
bank-ref: "DEP123"
Expenses:Commission 9.99 USD
Income:CapitalGains ; -350 USD gain computed
Validation Errors¶
Posting validation produces errors in these conditions:
| Condition | Error Type |
|---|---|
| Transaction does not balance | ValidationError |
| Multiple postings missing amounts for same currency | ValidationError |
| Account not opened | ValidationError |
| Account closed before posting date | ValidationError |
| Currency not allowed in account | ValidationError |