Skip to main content

guides/livebook/getting_started.livemd

# Getting started with beancount_ex

```elixir
Mix.install([
  {:beancount_ex, "~> 0.6"},
  {:kino, "~> 0.13"}
])
```

## Build a ledger

Constructors live on `Beancount`. You build a list of typed directive structs.

```elixir
ledger = [
  Beancount.open(~D[2026-01-01], "Assets:Bank", ["USD"]),
  Beancount.open(~D[2026-01-01], "Income:Salary", ["USD"]),
  Beancount.open(~D[2026-01-01], "Equity:Opening", ["USD"]),
  Beancount.transaction(~D[2026-01-31], "*", "Employer", "Salary", [
    Beancount.posting("Assets:Bank", Decimal.new("5000"), "USD"),
    Beancount.posting("Income:Salary", Decimal.new("-5000"), "USD")
  ])
]
```

## Render to Beancount text

```elixir
IO.puts(Beancount.render(ledger))
```

## Check the ledger

With the default CLI engine you need `pip install beancount beanquery` (the
latter provides `bean-query` for reports). The native engine works without
Python tooling:

```elixir
# Optional: use native validation (booking, balance assertions, pad)
Application.put_env(:beancount_ex, :engine, Beancount.Engine.Elixir)

case Beancount.check(ledger) do
  {:ok, result} -> result.status
  {:error, result} -> result.normalized.errors
end
```

## Parse existing text

```elixir
bean = Beancount.render(ledger)
{:ok, parsed} = Beancount.parse_text(bean)
Beancount.render(parsed) == bean
```

## Reports preview

```elixir
Application.put_env(:beancount_ex, :engine, Beancount.Engine.Elixir)

{:ok, result} = Beancount.balances(ledger)
Beancount.Query.Result.to_maps(result)
```

## Next notebooks

- [Accounting cookbook](accounting.livemd) - cash, salary, trading patterns
- [Parsing and validation](parsing.livemd) - import, round-trip, errors
- [Reporting with Explorer](reporting.livemd) - DataFrames and dashboards