Skip to content

robinvdvleuten/beancount

Repository files navigation

beancount

beancounting-gopher

A fast, lightweight Beancount parser and formatter written in Go.

Latest Release Build Status GPL-2.0 license PkgGoDev Go ReportCard

What is it?

Beancount is a double-entry bookkeeping system that uses plain text files to track personal or business finances. It allows you to maintain your accounting ledger as readable text files, making it easy to version control, search, and programmatically manipulate your financial data. The Beancount file format uses a simple, human-readable syntax to record transactions, accounts, and other financial directives.

This project is a Go implementation of a Beancount file parser, formatter, and validator. While the official Beancount implementation is written in Python and includes a full accounting engine with balance calculations, reports, queries, and a web interface, this Go version focuses on parsing, formatting, and ledger validation. It's designed to be fast and lightweight, making it ideal for tooling, text editors, build pipelines, or any situation where you need to validate Beancount files without the overhead of the full accounting system.

Features

This implementation currently supports:

  • Parsing: Full parsing of Beancount file syntax
  • Formatting: Auto-align currencies, numbers, and accounts
  • Validation: Balance checks, account lifecycle, assertions
  • Inventory: Lot-based tracking with cost basis (FIFO/LIFO)
  • Includes: Recursive loading of modular Beancount files
  • CLI Interface: Simple command-line tools for common operations

Note: This implementation includes ledger validation with transaction balancing, account management, and inventory tracking. It does not include reporting, queries, or a web interface like the official Python implementation.

Compatibility

This Go implementation is stricter than the official Python Beancount parser in some areas:

  • Arithmetic expressions in amounts must be enclosed in parentheses: (155.74 + 304.58) USD instead of 155.74 + 304.58 USD
  • Amount values must include both a number and currency: 100.00 USD (not just 100.00)

Installation

Packages & Binaries

If you use Brew, you can simply install the package:

brew install robinvdvleuten/tap/beancount

Or download a binary from the releases page. Linux (including ARM) binaries are available, as well as Debian, RPM AND APK packages.

Build From Source

Alternatively you can also build beancount from source. Make sure you have a working Go environment (Go 1.24 or higher is required). See the install instructions.

To install beancount, simply run:

go install github.com/robinvdvleuten/beancount

Usage

Check a Beancount file

Validate a Beancount file with full ledger checks:

beancount check example.beancount

Or read from stdin (omit filename or use -):

echo "2024-01-01 open Assets:Checking USD" | beancount check

This command validates:

  • Transaction balance across all currencies
  • Account open/close dates are respected
  • Balance assertions match actual balances
  • All referenced accounts exist

Example error output:

example.beancount:15: Transaction does not balance: (-500.00 USD)

   2020-01-15 * "Grocery shopping"
     Assets:Checking   1000.00 USD
     Expenses:Food      500.00 USD

1 validation error(s) found

Format a Beancount file

Format a Beancount file with automatic alignment:

beancount format example.beancount

# Specify currency column position
beancount format --currency-column 60 example.beancount

# Customize account name and number widths
beancount format --prefix-width 50 --num-width 12 example.beancount

Or read from stdin (omit filename or use -):

echo "2024-01-01 open Assets:Checking USD" | beancount format

Telemetry

Use the global --telemetry flag to see detailed timing breakdowns for any command:

beancount --telemetry check example.beancount
beancount --telemetry format example.beancount

This displays a hierarchical breakdown of where time is spent during execution:

✓ Check passed

check example.beancount: 125ms
├─ loader.load example.beancount: 85ms
│  └─ loader.parse: 85ms
│     ├─ parser.lexing: 75ms
│     ├─ parser.parsing: 8ms
│     ├─ parser.push_pop: ~1ms
│     └─ parser.sorting: 245µs
├─ loader.load accounts.beancount: 35ms
│  └─ loader.parse: 35ms
│     ├─ parser.lexing: 30ms
│     ├─ parser.parsing: ~4ms
│     ├─ parser.push_pop: 823µs
│     └─ parser.sorting: 156µs
├─ ast.merging: ~2ms
└─ ledger.processing (1523 directives): ~3ms

The telemetry output is written to stderr, making it easy to separate from command results.

Programmatic Usage

This library can be used programmatically in your Go applications to parse, manipulate, and generate Beancount files.

Parsing Beancount Files

Load and parse a Beancount file:

import (
    "context"
    "github.com/robinvdvleuten/beancount/loader"
)

// Load a single file
ldr := loader.New()
ast, err := ldr.Load(context.Background(), "example.beancount")
if err != nil {
    log.Fatal(err)
}

// Load with recursive include resolution
ldr = loader.New(loader.WithFollowIncludes())
ast, err = ldr.Load(context.Background(), "main.beancount")

Building Transactions Programmatically

Create Beancount transactions using the builder API:

import "github.com/robinvdvleuten/beancount/ast"

// Create a transaction with the functional options pattern
date, _ := ast.NewDate("2024-01-15")
checking, _ := ast.NewAccount("Assets:Checking")
groceries, _ := ast.NewAccount("Expenses:Groceries")

txn := ast.NewTransaction(date, "Grocery shopping",
    ast.WithFlag("*"),
    ast.WithPayee("Whole Foods"),
    ast.WithTags("food", "weekly"),
    ast.WithPostings(
        ast.NewPosting(groceries, ast.WithAmount("125.43", "USD")),
        ast.NewPosting(checking), // Balancing posting
    ),
)

Formatting Output

Format transactions back to Beancount syntax:

import (
    "context"
    "os"
    "github.com/robinvdvleuten/beancount/formatter"
)

// Create a formatter
fmtr := formatter.New()

// Format a single transaction
fmtr.FormatTransaction(txn, os.Stdout)

// Format an entire AST
fmtr.Format(context.Background(), ast, sourceContent, os.Stdout)

Complete Example

See the CSV Importer example for a complete working example that demonstrates:

  • Reading CSV files with Go's encoding/csv
  • Building transactions programmatically with the builder API
  • Automatic expense categorization
  • Error handling and validation
  • Formatting output

Run the example:

cd _examples/csv_importer
go run main.go transactions.csv

For more details on the builder API, see the package documentation.

Contributing

Everyone is encouraged to help improve this project. Here are a few ways you can help:

To get started with development:

git clone https://github.com/robinvdvleuten/beancount.git
cd beancount
go test ./...

Before submitting a pull request, please make sure to run go fmt on any Go source files you touched so the code stays consistent.

Feel free to open an issue to get feedback on your idea before spending too much time on it.

License

Copyright (c) Robin van der Vleuten

Licensed under the GNU General Public License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

https://www.gnu.org/licenses/old-licenses/gpl-2.0.html

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

About

Fast, lightweight Beancount parser, formatter and editor written in Go

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors