hledger
plain text accounting.
hledger is friendly, fast, and dependable accounting software for tracking money, investments, cryptocurrencies, time, or any countable commodity. It uses human-readable plain text data that you control.
You can import CSV from banks, or enter data via web/terminal interfaces or your favorite text editor. hledger provides powerful reports and scales smoothly from simple to sophisticated needs. It works well with version control, scripts, and LLMs. Read more at Why hledger ? and FAQ.
A quick example
Transactions are recorded in a journal file. You can edit this file yourself, or use one of several user interfaces. This simple format, invented by the Ledger project, is the key to understanding Plain Text Accounting and Double Entry Bookkeeping:
; ~/.hledger.journal
2025-12-01 Starting balance
equity $-1000 ; <- $1000 moves from the "equity" account
assets:checking $1000 ; <- to the "assets:checking" account
2025-12-02 Grocery store
assets:checking ; <- amount omitted, $-64.50 is implied
expenses:groceries $64.50
2025-12-03 Client payment
income:consulting
assets:checking $1500
2025-12-04 Rent
assets:checking
expenses:rent $800
This file is all you need. From it, hledger generates reports:
$ hledger aregister assets
Transactions in assets and subaccounts:
2025-12-01 Starting balance equity $1000.00 $1000.00
2025-12-02 Grocery store ex:groceries $-64.50 $935.50
2025-12-03 Client payment in:consulting $1500.00 $2435.50
2025-12-04 Rent ex:rent $-800.00 $1635.50
$ hledger balance
$1635.50 assets:checking
$-1000.00 equity
$64.50 expenses:groceries
$800.00 expenses:rent
$-1500.00 income:consulting
--------------------
0
$ hledger is --pretty --tree --daily --row-total --average --begin 2025/12/2
Daily Income Statement 2025-12-02..2025-12-04
┌───────────────────╥────────────┬────────────┬────────────┬──────────┬─────────┐
│ ║ 2025-12-02 │ 2025-12-03 │ 2025-12-04 │ Total │ Average │
╞═══════════════════╬════════════╪════════════╪════════════╪══════════╪═════════╡
│ Revenues ║ │ │ │ │ │
├───────────────────╫────────────┼────────────┼────────────┼──────────┼─────────┤
│ income:consulting ║ 0 │ $1500.00 │ 0 │ $1500.00 │ $500.00 │
├───────────────────╫────────────┼────────────┼────────────┼──────────┼─────────┤
│ ║ 0 │ $1500.00 │ 0 │ $1500.00 │ $500.00 │
╞═══════════════════╬════════════╪════════════╪════════════╪══════════╪═════════╡
│ Expenses ║ │ │ │ │ │
├───────────────────╫────────────┼────────────┼────────────┼──────────┼─────────┤
│ expenses ║ $64.50 │ 0 │ $800.00 │ $864.50 │ $288.17 │
│ groceries ║ $64.50 │ 0 │ 0 │ $64.50 │ $21.50 │
│ rent ║ 0 │ 0 │ $800.00 │ $800.00 │ $266.67 │
├───────────────────╫────────────┼────────────┼────────────┼──────────┼─────────┤
│ ║ $64.50 │ 0 │ $800.00 │ $864.50 │ $288.17 │
╞═══════════════════╬════════════╪════════════╪════════════╪══════════╪═════════╡
│ Net: ║ $-64.50 │ $1500.00 │ $-800.00 │ $635.50 │ $211.83 │
└───────────────────╨────────────┴────────────┴────────────┴──────────┴─────────┘
or runs other actions:
$ hledger add
Adding transactions to journal file /Users/simon/.hledger.journal
...
Date [2025-12-13]:
Description: groc
Using this similar transaction for defaults:
2025-12-02 Grocery store
assets:checking $-64.50
expenses:groceries $64.50
Account 1 [assets:checking]:
Amount 1 [$-64.50]: _
$ hledger web
...
Serving web UI and json API at IP address 127.0.0.1 (local access), port 5000
with base url http://127.0.0.1:5000
This server will exit after 2m with no browser windows open (or press ctrl-c)
Opening web browser...
$ hledger check --strict
hledger: Error: /Users/simon/.hledger.journal:2:
| 2025-12-01 Starting balance
2 | equity $-1000
| ^^^^^^
| assets:checking $1000
Strict commodity checking is enabled, and
commodity "$" has not been declared.
Consider adding a commodity directive. Examples:
commodity $1000.00
commodity 1.000,00 $
$ hledger
__ __ __
/ / / /__ ___/ /__ ____ ____
/ _ \/ / -_) _ / _ `/ -_) __/
/_//_/_/\__/\_,_/\_, /\__/_/
/___/
-------------------------------------------------------------------------------
hledger 1.51.99-g67b206380-20251207, mac-aarch64
Usage: hledger [COMMAND] [OPTIONS] [ARGS]
Commands:
...
Get started
- Install - quickly get hledger installed. Here are release notes.
- Docs - many learning resources. hledger by example is a good place to start.
- Support - ask questions or share feedback.
Get involved
- Star us on Github to help make us a notable Haskell app!
- Sponsor hledger to help sustain this work. Here’s more about Finance.
- Contributor Quick Start and Developer docs.
- CREDITS shows many of the people who have contributed code over the years.
Access keys:
1 home,
2 changes,
s sidebar,
t theme,
f find,
p print,
r repo,
e edit
You can also press the left or right arrow keys to step through pages.
Install
The current hledger release is 1.52. Here are the release notes.
You can install hledger by any of these methods:
- Install official binaries from Github (quick)
- Install packaged binaries (quick; not always up to date)
- Install binaries you build from source (takes longer)
and then, check your setup.
Official binaries
Official release binaries for Linux, Mac, and Windows are provided in the hledger github repo. You can click the badge below to install them manually, or copy-paste the install command from Get hledger installed, or use a download tool like eget:
eget simonmichael/hledger --all
Packaged binaries
Homebrew (Mac, Linux)
brew install hledger
Docker (Linux, Mac, Windows) (more)
docker pull dastapov/hledger
Windows
scoop install hledger
winget install -e --id simonmichael.hledger
choco install hledger -y
BSD
pkg install hs-hledger hs-hledger-ui hs-hledger-web
pkg_add hledger
Nix (Linux, Mac)
(Troubleshooting,
#1030,
#1033,
#2089)
nix-shell -p hledger hledger-ui hledger-web
GNU/Linux
Alpine
doas apk add hledger hledger-ui hledger-web
Arch
pacman -Sy hledger hledger-ui hledger-web
Debian (more):
apt install hledger hledger-ui hledger-web
Fedora (more)
dnf install hledger
Gentoo
eselect repository enable haskell && emerge hledger hledger-ui hledger-web
Raspberry Pi (unaudited)
hledger-linux-arm32v7.zip
hledger-aarch64-manjaro.gz ,
hledger-armhf32-debian.gz
Ubuntu (more)
apt install hledger hledger-ui hledger-web
Void
xbps-install -S hledger hledger-ui hledger-web
Sandstorm (web)
HLedger Web sandstorm app
Build from source
Building hledger requires the GHC compiler and either the stack or cabal build tool which you can install with your package manager (brew, apt, winget..), with ghcup, or with stack (simplest). Or, you can use docker. All this may need perhaps 4G of RAM and 4G or more of disk space.
On Mac
You will need the XCode Command Line Tools. Homebrew or macports will probably also be helpful.
Old issues:
On Unix/Linux
You will need
-
The header files for certain C libraries, which stack/cabal can’t install for you; otherwise you’ll see build errors like “cannot find -ltinfo”. The exact package names will be specific to your system, but here are some likely install commands:
- Debian-based systems:
apt install libgmp-dev libncurses-dev zlib1g-dev - Older Debian systems:
apt install libgmp3-dev libncurses5-dev zlib1g-dev - Redhat-based systems:
dnf install gmp-devel ncurses-devel zlib-devel - Arch:
pacman -S gmp ncurses zlib - Alpine:
apk add gmp-dev ncurses-dev zlib-dev - openSUSE:
zypper install gmp-devel ncurses-devel zlib-devel - FreeBSD:
pkg install gmp ncurses
- Debian-based systems:
-
A configured system locale that specifies a text encoding; otherwise you’ll see text decoding errors when processing non-ascii characters. For example, on most unix systems
echo $LANGshould show something likeen_US.UTF-8orzh_CN.GB2312orC.UTF-8- it should not be justC, or unset. This is discussed more in Text encoding, below.
Get the hledger source code with git:
git clone https://github.com/simonmichael/hledger
cd hledger
git checkout 1.52 # switch to the latest release tag (optional)
Then build and install with stack:
stack update; stack install
or with cabal:
cabal update; cabal install all:exes
or with docker:
cd docker; ./build.sh # or build-dev.sh to keep build artifacts
Old issues:
- arch: haskell build advice from Arch wiki
- openbsd 6: exec: permission denied
- openbsd: stack install tips
On Windows
These notes are for Windows 11. On Windows, stack is the easiest way to get the haskell tools. (Though if you are on a Windows ARM machine, stack will install slow x86_64 versions of the tools, and build slow x86_64 hledger binaries.)
First, apply all windows updates (to get the latest TLS certificates for network requests).
Install stack - in a command or powershell window, run:
winget install -e --id commercialhaskell.stack
Install git:
winget install -e --id Git.Git
Get the hledger source:
git clone https://github.com/simonmichael/hledger
cd hledger
git checkout 1.52 # switch to the latest release tag (optional)
Build and install hledger:
stack update
stack install
On Windows, this may die repeatedly with a “… permission denied (Access is denied.)” error; we don’t know why. Just run it again to continue (press up arrow, enter).
On Windows, things work best if you build in the environment where you will use hledger. Eg don’t build it in a WSL or MINGW window if you plan to use it in CMD or Powershell.
Old issues:
On Nix
Old issues:
- nix: nix install on linux can fail with “cloning builder process: Operation not permitted”
- nix: on Linux, nix-installed hledger won’t handle non-ascii data
On Android
Here’s how to build hledger on Android with Termux, if your phone has plenty of memory.
Build tips
- Building the hledger tools and possibly all their dependencies could take anywhere from a minute to an hour.
- On machines with less than 4G of RAM, the build may use swap space and
take much longer (overnight), or die part-way through.
In such low memory situations, try adding
-j1to the stack/cabal install command, and retry a few times, or ask for more tips. - You could build just the hledger CLI to use less time and space: instead of
stack install, runstack install hledger - It’s ok to kill a build and rerun the command later; you won’t lose progress.
- You can add
--dry-runto the install command to see how much building remains.
Check your setup
With modern hledger versions, you should now run:
hledger setup
to check your installation. If this doesn’t work, read on..
PATH
After installing, try to run the hledger tools (hledger, hledger-ui, hledger-web) and look for the expected versions. Eg:
$ hledger --version
hledger 1.52-...-20260320, mac-aarch64
If this doesn’t work, you may need to add the binaries’ install directory to your shell’s PATH.
stack or cabal show the install directory in their output, and warn you if it is not in PATH. It could be, eg:
~/.local/binorC:\Users\USER\AppData\Roaming\local\bin\(stack)~/.cabal/binorC:\Users\USER\AppData\Roaming\cabal\bin\(cabal)
On unix, these commands will add both bin directories to PATH permanently (probably):
echo "export PATH=~/.local/bin:~/.cabal/bin:$PATH" >> ~/.profile
source ~/.profile
On Windows, here’s how to set environment variables.
Text encoding
Data files containing non-ascii characters are saved with a text encoding - UTF-8, Latin-1, CP-437, or something else. hledger uses the system’s text encoding when reading data, and it expects data to use the same encoding. So if no system encoding is configured, or if the data uses a different encoding, hledger will give an error when reading it.
TLDR: run hledger setup to check this, and ensure your data files use the encoding it recommends.
Here’s more detail:
How likely is this to affect you ? It depends on your platform and the data you are working with:
-
On Mac, the system encoding is always UTF-8. You may see this problem if you are working with files received from another system, eg from a Windows system.
-
On Windows, the system encoding varies by region. You probably won’t see this problem if you are working with your own data (perhaps depending how you create the data - see Start a journal). If you are on Windows 11 and often need to share files with mac/unix systems, there is a setting for UTF-8 encoding which you might want to use (see below).
-
On GNU/Linux and other unix systems, the system encoding varies, and sometimes is not configured at all. It may be controlled by the
LANGenvironment variable, or in other ways. You should ensure that at least some encoding is configured. UTF-8 is usually a good choice.
If you hit this problem, you can solve it by
- converting the data files to your system’s text encoding. Use
iconvon unix/mac, powershell or notepad on Windows. - configuring your system encoding to match your data files.
- or (for CSV/SSV/TSV files only), use the
encodingCSV rule.
Here’s an example. Let’s say you want to work with UTF-8 text on a GNU/Linux system, but it’s configured with the C locale, which can only handle ASCII text:
$ echo $LANG
C
So, first check that you have a UTF-8-capable locale installed (locale -a).
If not, install one (perhaps by using your package manager,
perhaps by uncommenting it in /etc/locale.gen and running locale-gen).
Then change the system locale. Here’s one common way to set it permanently for your shell. Note exact punctuation and capitalisation of locale names is important on some systems.
$ echo "export LANG=C.utf8" >>~/.profile # or en_US.UTF-8, fr_FR.utf8, etc.
# close the shell/terminal window and open a new one
$ echo $LANG
C.UTF-8
For Nix users, the procedure is different,
eg you might need to set LOCALE_ARCHIVE instead.
Likewise for GUIX users.
Windows users who want to use UTF-8 encoding, eg to interoperate with unix systems,
might find the “Use Unicode UTF-8 for worldwide language support” setting helpful.
Here’s where it is in Windows 11:
Though it might cause problems with some older applications, including some GUI programs.
Here’s a way to select UTF-8 for Windows Terminal and PowerShell, without affecting the entire system: add this line to the PowerShell profile file:
$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding
Completions
If you use the bash or zsh shells, you can set up context-sensitive auto-completions for hledger command lines. (Here’s how to contribute other shells.)
bash
-
Ensure that bash-completion is installed and enabled:
On a Mac, using homebrew:
brew remove -f bash-completionbrew install bash-completion@2- Add the suggested line to your ~/.bash_profile, if it’s not already there
source ~/.bash_profile(or open a new bash shell)
On GNU/Linux:
apt install bash-completionsource ~/.bash_profile(or open a new bash shell)
-
Install hledger with your system package manager (
brew install hledger,apt install hledgeror similar).Now completions may be working.
If not, eg because your system’s hledger package does not yet include the bash completions, or if they are not up to date, or if you have installed hledger by other means, then install the latest hledger bash completions yourself, under your XDG_DATA_HOME directory. Eg:
curl https://raw.githubusercontent.com/simonmichael/hledger/1.50-branch/hledger/shell-completion/hledger-completion.bash \ -o ~/.local/share/bash-completion/completions/hledger --create-dirs
Here’s what the bash completions should complete when you press TAB once or twice in a command line:
Before the command argument:
- the “hledger”, “hledger-ui” and “hledger-web” executable names
- general flags and flag values
- hledger’s command argument.
Eg
hledger <TAB><TAB>should list all hledger commands, andhledger b<TAB><TAB>should list the ones starting with b.
After the command argument:
- command-specific flags and flag values
- account names
- query prefixes, like
payee:orstatus: - valid query values after these query prefixes:
acct:,code:,cur:,desc:,note:,payee:,real:,status:,tag:. Eghledger reg acct:<TAB><TAB>should list your top-level account names. - amount comparison operators after
amt:.
When a completion includes special characters, backslashes will be inserted automatically; this does not work right in all cases.
zsh
- Ensure that zsh-completions is installed and enabled.
- ? discussion
Next steps
Nicely done! Now see Docs, or come to the #hledger chat where we’ll gladly share tips or receive your feedback.
Release notes
Major releases and user-visible changes.
2026-03-20 hledger-1.52
Preserve cost basis syntax, cost basis export examples, faster valuation, commodity tags, more robust paging, fixes.
hledger 1.52
Features
-
aregisterandregisternow support--dropfor trimming leading account name components, like thebalancecommand. (Caleb Maclennan) -
printnow preserves and reproduces Ledger-style lot syntax (cost basis annotations) in text and json output, and (converted to Beancount syntax) in beancount output. -
Tags can now be declared on commodities, and you can query for postings by their commodity’s tags.
-
A new
Gain(G) account type has been added, as a subtype of Revenue. Certain account names, likeincome:gains,revenue:capital-gains,income:losses, are auto-detected as Gain type. This provides a language-independent way of matching capital gain/loss accounts specifically. (It is used more in hledger 2.) (g. nicholas d’andrea) #2522
Fixes
-
The
addandimportcommands once again read all-ffiles, not just the first. This fixes a regression in 1.51.2 which broke autocompletion inadd, and multi-file reading inimport, when multiple-foptions were given. #2553 -
In balance reports, accounts revealed by
--empty --declarednow respect account display order, instead of being shown last. (Juliano Solanho) #2564 -
balance --budget’s csv/tsv output now properly suppresses digit group marks (eg thousands separators), preserving valid CSV structure. #2555 -
The
runcommand now properly returns a non-zero exit code if there’s an error while commands are being provided on standard input. (Previously it always returned exit code 0 in this mode.) #2557 -
Options requiring a value (like
--roundor-f) now give a clear error if the value is missing (rather than trying to consume a following flag). #2556 -
Postings generated by
--infer-equityno longer inherit the source posting’s tags, comment, or real/virtual type. (The posting’s date and status are still inherited.) #2535
Improvements
-
When converting to value, price lookups are now optimised with pre-built indexes. This replaces O(n log n) re-sorting on every valuation date with O(log n) indexed lookups, significantly improving performance for
--value=end,COMMwith daily reports over long periods and large price databases. (Oleg Bulatov) #2511 -
date:queries can now include a report interval, egdate:weeklyordate:'weekly from last month', like the-p/--periodoption. -
Smart dates now understand
last|this|next WEEKDAYandlast|this|next MONTHNAME. -
When
addorimportare autocreating a requested journal file that did not exist, they will also create any required parent directories. -
The less pager is now invoked more robustly; we catch and report more kinds of failure clearly, and/or fall back to unpaged output with a warning. #2544
-
The
--quit-at-eofflag is no longer added when running the less pager (and our less flags are better documented). -
Improvements to the
setupcommand:- improve top info’s layout
- show the OS version, architecture, and compiler version
- show if hledger is wrongly built without OS thread support
- show the value of $LESS more accurately
- test that it runs with the configured options
- print a warning before making a http request
- show more compact output if the http request fails
Docs
- Account tags: new separate section
- COMMON TASKS: Setting LEDGER_FILE: updates
- Cost basis / lot syntax: new section and edits
- Costs: rewrite
- csv: if: field matchers: clarify
- Inferring equity conversion postings: note account tags limitation
- journal: code: mention valid characters, recommend tags #2563
- Regular expressions: note no lazy quantifiers
- Tag names: clarify –verbose-tags
Examples
- csv: Fidelity, Open Collective updates
- csv: Interactive Brokers example CSV rules files #2508 (Ilja Kocken)
- csv/cctax: notes and sample files related to exporting to cryptocurrency tax calculators
- debconf: DebConf ledger files 2017-2025, adapted for hledger
- investing/export-lots-workflow: doc and examples for exporting to Beancount, Ledger or rustledger for lots/gains calculation
Scripts/addons
- bashrc: drop clashy month aliases; fix LEDGER_FILE typo; cleanup.
- fix compilation errors in bin/ scripts (Dmitry Astapov) #2497
hledger-smooth: accept ACCTPAT, matching case-insensitively as infix (likearegister).hledger-fancyassertions: useshowMixedAmountfor properly formatted output. (Joshua Chapman)ledgereval: evaluate Ledger value expressions at the command line
hledger-ui 1.52
Fixes
-
List screens with no items now correctly appear empty on all platforms. (An unguarded division by zero was disrupting the display on non-ARM machines.) (Tuong Nguyen Manh, Simon Michael) #2476, #2550
-
The less pager (used for displaying help, eg) is now invoked more robustly; we catch and report more kinds of failure clearly. #2544
Improvements
-
New capital
J/Kkeybindings move down/up 10 rows at a time. (Rahul Shankar V, Simon Michael) #1911, #2551 -
The
defaulttheme has been renamed tolight. (Rahul Shankar V, Simon Michael) #2168, #2551 -
The selection colour has been changed to cyan, for better visibility in typical terminals. (Rahul Shankar V, Simon Michael) #2175, #2551
hledger-web 1.52
Fixes
- The less pager (used for displaying help, eg) is now invoked more robustly; we catch and report more kinds of failure clearly. #2544
Docs
- openapi.yaml (the OpenAPI spec for hledger-web’s JSON API) has been updated. (n0vdd)
project changes 1.52
Doc updates
- ACHIEVEMENTS, ANNOUNCE, README, REGRESSIONS, RELEASING
- Github issue template improvements
Infrastructure/tools
- fix URLs in github test binaries release doc
- github issue templates updated
- Justfile scripts
- specify and require (newish) bash as default shell #2527
- depend less on ghc in PATH
- add compile.sh to build Haskell scripts
- just ghrel-bin-upload -> ghrel-upload, fix
- just google-search-console
- just perfhelp (Simon Michael, Marko Kocić)
- just perftest
- just test: run doctests last to reduce double compilation (Marko Kocić) #2528
- just tools
- replace gsed with sed (Marko Kocić)
- github workflows
- binaries-windows: update for new GHC
- binaries-linux: caching, cleanup improvements
- nightly -> testbin
- ensure linux binary uses fixed haskeline #2410
- testbin: update, use testlatest tag / testbin branch
credits 1.52
Simon Michael, Caleb Maclennan, Dmitry Astapov, Marko Kocić, RahulShankarV52, Tuong Nguyen Manh, Ilja Kocken, Jens Petersen, Joshua Chapman, Juliano Solanho, Oleg Bulatov, g. nicholas d’andrea, n0vdd.
2026-01-08 hledger-1.51.2
hledger 1.51.2
Fixes
-
The
addandimportcommands now once again auto-create the journal file if it does not exist yet, fixing a regression in 1.50.3. Also they now create it lazily, only when they have data to write, not unconditionally at the start. #2514 -
The
roicommand has some more sanity checks, and some error messages have been clarified. (Dmitry Astapov, #2505)
Improvements
-
The
-foption now reports an error if you give it a glob pattern (a path containing[,{,*, or?) that matches nothing. This makes it consistent withLEDGER_FILE. -
Journal format’s
includedirective no longer unnecessarily reads the attributes of all files in a directory. This works better with build tools like tup which detect filesystem operations. -
Journal format’s
includedirective has been optimised, repairing a slight slowdown introduced in 1.50.3. It no longer callscanonicalizePathunnecessarily. This might be noticeable with many includes on a slow filesystem. -
Allow base 4.22 / ghc 9.14.
API
- Hledger.Cli.Utils: withPossibleJournal
hledger-ui 1.51.2
-
hledger add invoked via the
akey now shows output properly, fixing a regression in 1.50. #2512 -
Allow vty-crossplatform 0.5. (Jens Petersen)
-
Allow base 4.22 / ghc 9.14.
hledger-web 1.51.2
-
The register view now prevents line wrapping in dates when the window is narrow. #2520
-
Allow base 4.22 / ghc 9.14.
credits 1.51.2
Simon Michael, Dmitry Astapov, Jens Petersen.
2025-12-08 hledger-1.51.1
hledger 1.51.1
Fixes
-
Relative includes from a symbolically-linked journal file now work again. This was fallout from 1.50.4’s fixes. #2503
-
When journal’s include directive has an IO error, like trying to include an existing but unreadable file, or failing to find a home directory when expanding ~, it now shows the problematic include directive (previously the line number was off by one).
-
aregister: respect the order of -f options when showing same-day transactions from multiple files. If transactions on the same date are coming from two files specified with -f options, we expect them to be displayed in parse order, ie respecting the order of the -f options. This wasn’t always the case, now it is. -
aregister: show “ACCTPAT matches no account” error on just one line. -
Fix build failures with the scripts in bin/. (Dmitry Astapov, #2497)
hledger-ui 1.51.1
- Uses hledger 1.51.1.
hledger-web 1.51.1
- Uses hledger 1.51.1.
credits 1.51.1
Simon Michael, Dmitry Astapov,
2025-12-08 hledger-1.50.5
hledger 1.50.5
Fixes
- Relative includes from a symlinked file work again, fixing some fallout from 1.50.4’s fixes. #2503
hledger-ui 1.50.5
- Uses hledger 1.50.5.
hledger-web 1.50.5
- Uses hledger 1.50.5.
credits 1.50.5
Simon Michael.
2025-12-05 hledger-1.51
Small improvements, doc updates
hledger 1.51
Fixes
- HTML output no longer contains invalid nested tables (Joschua Kesper). This may cause some visual changes, hopefully all for the better.
stats:-onow redirects all output, including the performance stats.
Features
commodities,payeesandtagscommands now have a--findmode for finding the best match (like theaccountscommand).stats: A new-1flag prints a single line of output in machine-friendly tab-separated format, including the program version, journal file name, and performance stats.
Improvements
- In journal format, an empty
{}pair is now allowed (and ignored) in amounts, slightly improving Ledger/Beancount compatibility. accountsin--treemode, when showing a subset of accounts, now shows parent accounts for context, like thebalancecommand. (Sam Almahri, #2427)accounts --typesno longer shows value-less type tags; untyped accounts are shown without atypetag.- When pivoting on account type with
--pivot=type, account types will be normalised to their short spelling (Ainstead ofAssets, etc). accounts: the--positionsflag has been renamed to--locations.print: the--locationflag has been renamed to--locations.setup: tidier output for missing LEDGER_FILE or default file; wording improvements
Docs
- Account names: rewrite, emphasise the two space delimiter
- accounts: improve/sync options order
- add: balance assertions/assignments: clarify, fix #2494
- aregister: clarify arguments
- argument files: corrections
- check: drop obsolete note about transaction balancing
- close: customisation: clarify #2492
- commodity directive: add info about the -c command line option (Ooker)
- COMMON TASKS: demote subheadings
- csv: “CSV fields and hledger fields”; “Regular expressions in CSV rules”; link the
if-specificskipandendrules more clearly - depth: rewrite, note combining issue
- Editor configuration -> Editors
- print: clarify amount styling; note more parseability breakers
- Setting LEDGER_FILE: rewrite, new windows procedures
- Special characters: rewrite (Simon Michael, Caleb Maclennan, #2468)
- Tags: rewrite [hledger_site#141]
- Value reporting: more advice, examples for COMM, warn about -V/emphasise -X
Examples
- Organise/rename examples/csv/ as the CSV rules library
Scripts/addons
hledger-check-buynothing: check Buy Nothing Day compliancehledgerj1: example of a wrapper for reading a custom data formatsortandmergepostings: Overhaul for more robust determinism (Caleb Maclennan)- Avoids non-deterministic flip-flopping when the alphabetical account sort has multiple commodities
- Sorts postings commodities so commodities are in the same order across transactions
- Sorts postings with matching commodity by posting amount
API
- Hledger.Cli.Utils:
- stop exporting pivotByOpts, anonymiseByOpts
- renamed withJournalDo -> withJournal. The old name is still available but deprecated.
hledger-ui 1.51
Improvements
-
Allow brick 2.10, vty 6.5.
-
Uses hledger 1.51.
hledger-web 1.51
Improvements
- Uses hledger 1.51.
project changes 1.51
Docs updated
- ACHIEVEMENTS
- ANNOUNCE.short
- AUTHORS
- bin/README
- CHANGES.md: move to doc/ for consistent paths
- Contributing: mention translation (Lý Minh Nhật)
- DECISIONS
- dev
- DOCS: document & diagram latest doc update procedures
- ISSUES
- REGRESSIONS
- RELEASING
- relnotes: more reliable changelog links
- ROADMAP
- tldr/README
Infrastructure/Misc
- bump stack snapshots
- Dockerfile fixed (probably)
- drop the hledger-install script
- just changelogs* update
- just docupdatediag
- just ghci-ui: :main (and :rmain) now works again
- just ghnightly* -> nightly*
- just oldest
- just perftest: run performance tests, logged to perf.log
- just releasediag update
- just rels-major
- just site-restart
- just: devtag-push: simplify
- just: ghrel-bin-upload
- just: manuals, manuals-site
- just: reltags: cleanup
- just: ver, majorver
- Justfile: less safari dependence
- Justfile reorg, cleanups
- release script updates
- Shake: drop mandates target, update manual dates automatically
- Shake manuals: more contributor instructions in the header comment [hledger_site#140]
- stack92: add valid hashtables version
- stack94: add valid hashtables version
- stack96: add valid hashtables version
- stack98: include valid hashtables version
- stack98: restore needed encoding 0.10 extra dep
- stop git ignoring site/src/, allowing web docs to be easily opened in vscode
- tools/relnotes.hs update
- use a nightly branch instead of a nightly tag on github, it’s simpler
- workflows: use shelltestrunner 1.11
- workflows:binaries-mac-x64: switch to macos-15-intel
credits 1.51
Simon Michael, Caleb Maclennan, Joschua Kesper, Ooker, Sam Almahri.
2025-12-04 hledger-1.50.4
hledger 1.50.4
Fixes
-
An
includedirective with no argument now gives consistent error messages. -
journal format’s
includedirective no longer excludes paths containing dotted directories/files. 1.50-1.50.3 contained an overzealous workaround that sometimes wrongly excluded paths containing a dot dir or dot file. Now the pre-1.50 behaviour is restored (*and**generally avoid dot files and dot directories, except**will search non-top-level dot directories). #2498 -
Symbolic links found by
includedirectives are once again shown as-is, not dereferenced. (1.50-1.50.3 showed them dereferenced, eg inhledger filesoutput.)There is some related new behaviour: each time an include directive is parsed, all the parent file paths and the new include file path(s) are re-canonicalised. Previous hledger versions did not do this; it’s expected to be unnoticeable, but if you notice any slowdown caused by having many include directives and a slow filesystem, please report it.
hledger-ui 1.50.4
- Uses hledger 1.50.4.
hledger-web 1.50.4
- Uses hledger 1.50.4.
credits 1.50.4
Simon Michael.
2025-11-18 hledger-1.50.3
hledger 1.50.3
Fixes
-
hledger versions 1.50-1.50.2 ran much slower than normal (depending on the speed of your hard drive). This 1.50.3 release fixes that; so please upgrade, and avoid those older 1.50 releases. #2493
-
A regression in 1.50, where very large dates could produce wrong reports, has been fixed. (Affecting dates outside the range -25252734927764696-04-22..25252734927768413-06-12 on 64 bit machines, or -5877752-05-08..5881469-05-27 on 32 bit machines.) #2479
-
If the
LEDGER_FILEenvironment variable is set to a nonexistent file, we now report an error rather than silently falling back on a default file path, which was confusing. Also, we no longer support the legacyLEDGERenvironment variable as a fallback. #2485 -
setupnow shows tidier output when a LEDGER_FILE or default file is not found, -
addnow checks balance assertions more robustly, with awareness of how everything is ordered in the journal. Also, it now allows adding balance assignments. #2478 -
check accountsno longer garbles non-ascii account names in its output. #2469 -
We now escape special characters properly when passing arguments to addons (with just one level of quoting/escaping, not two). And related docs have been improved. (Caleb Maclennan, Simon Michael, #2468)
-
Internal report code which could produce certain date-related errors has been made robust again. (Stephen Morgan)
-
The old “threaded” build flag, which cabal could turn off, has been dropped. This will hopefully prevent wrong builds like Debian’s #1120833. #2495
-
Docs updated: add, areg, argument files, check, close, csv, depth, print, print, Special characters, Value reporting
hledger-ui 1.50.3
-
--watchmode now also detects changes from apps which overwrite the file, such as VS Code. (Caleb Maclennan) -
When hledger-ui is started with –pivot, re-enabling balance assertions with the I key now does a full journal reload, to check assertions more accurately. #2451
-
The old “threaded” build flag, which cabal could turn off, has been dropped. #2495
-
Allow brick 2.10, vty 6.5.
-
Uses hledger 1.50.3.
hledger-web 1.50.3
-
The old “threaded” build flag, which cabal could turn off, has been dropped. #2495
-
Uses hledger 1.50.3
credits 1.50.3
Simon Michael, Caleb Maclennan, Stephen Morgan.
2025-09-26 hledger-1.50.2
hledger 1.50.2
Fixes
-
The CSV
encodingrule is now respected when using the rules file as input file. This was a regression in 1.50. Also, the text decoding error message has been clarified; it now mentions a possible CSV encoding. #2465 -
The doc for
--depthhas been clarified, and now mentions the use of quotes. (Lý Minh Nhật, Simon Michael, [hledger_site#140]) -
A typo was fixed in the –depth example in
register’s doc. ([hledger_site#140], reported by Lý Minh Nhật) -
Our package bounds now avoid hashtables 1.3.x, which fails to build with some gcc versions (see https://github.com/gregorycollins/hashtables/issues/97)). (hseg, #2463)
hledger-ui 1.50.2
- Uses hledger 1.50.2
hledger-web 1.50.2
- Uses hledger 1.50.2
credits 1.50.2
Simon Michael (@simonmichael), hseg (@hseg).
2025-09-16 hledger-1.50.1
hledger 1.50.1
Fixes
-
Balance commands now show an empty report instead of an error when no transactions are matched, fixing a regression in 1.50. (Stephen Morgan, #2452)
-
The
printcommand’s help no longer shows an unused –show-costs flag. And the command-specific flags are now mostly ordered alphabetically. -
Whitespace in the
setupcommand’s “undeclared commodities” output has been fixed.
Doc updates
- Text encoding
- bin/README: paypal*, simplefin* usage examples, doc link
hledger-ui 1.50.1
Fixes
-
The transaction screen and error screen now update on data changes like other screens, eg when using the
Eorgkeys or--watch. #2014, #2288 -
When the journal is reloaded by the
gkey or--watch, the--pivot(and--obfuscate) options are now preserved, and spurious errors are avoided. #2451 -
The
Zkey (and the-Ecommand line flag) toggle zero-balance accounts again. (Stephen Morgan, #2454)
Improvements
- Debug output has improved, eg it’s easier to see changes to the screen stack.
hledger-web 1.50.1
- Uses hledger 1.50.1
credits 1.50.1
Simon Michael (@simonmichael), Stephen Morgan (@Xitian9).
2025-09-03 hledger-1.50
Better transaction balancing, include improvements, auto posting account interpolation, csv data commands, import archiving, timeclock improvements, fixes
hledger 1.50
Breaking changes
-
Transaction balancing is now done in a more robust way, using local precisions only (like Ledger) #2402. Until now, a transaction was required to balance using its commodities’s global display precisions. Small imbalances were tolerated by configuring display precisions for the whole journal (with
commoditydirectives).Now, a transaction is required to balance using the precisions in its journal entry only. This means each entry can use the precision it needs, and balancing precision and display precision are independent. (So eg, increasing the display precision with
-cno longer breaks the journal.)In practice this requires journal entries to be more accurate, and you will probably need to fix some old entries. There are three main ways to fix an entry:
- reduce the amounts’ precision (use fewer decimal digits, so a lower balancing precision is inferred)
- make the amounts more accurate (use better decimal digits, so the amounts sum to zero more closely)
- or (easiest) add an amountless “expenses:rounding” posting (this is not a cheat, it’s a more accurate record of what your bank/broker is doing).
You can also keep the old transaction-balancing behaviour with
--txn-balancing=old, for now. But updating your entries is recommended.The old behaviour could allow small remainders to accumulate over time, in accounts that often have an inexact posting amount or cost amount and are never reconciled - typically equity, revenues, and expenses. You can check for this in your old journals with a command like
hledger bal cur:\\$ -c '$1.000000000000' | grep -E '\...0*[1-9]'(show $ account balances, with many decimals, which have a non-zero decimal in the 3rd place or beyond)
-
Timeclock format has had various changes:
- Timeclock syntax and parsing is now more robust (when not using –old-timeclock):
- Semicolon always starts a comment (and timeclock account names may not include semicolons).
- Trailing spaces are ignored.
- Clock-ins now require an account name.
- Clock-outs now can have a comment and tags.
- Timeclock entries are processed in parse order.
- Some order-related bugs in 1.43 have been fixed.
- Concurrent/overlapping sessions are now fully supported, even if they have the same account name.
- The timeclock doc has been rewritten.
- The –old-timeclock hidden flag has been renamed, documented, and now also affects included files. #2141, #2365, #2400, #2417
- Timeclock syntax and parsing is now more robust (when not using –old-timeclock):
-
The
importcommand now shows info messages (such as the dry run “; would import ..” message) on stderr, not stdout. Also the “no new transactions” output is more compact, showing file names not file paths; and it no longer prints an extra newline. -
Some edge cases in balance report behaviour were changed for internal consistency:
-
hledger now requires at least GHC 9.6 (and base 4.18), to ease maintenance.
Fixes
-
Paging long output no longer gives an error when
LESSis undefined andlessdoes not have mouse support (as on some FreeBSD systems). -
The
all:query now requires at least one posting to match. (Previously, matching no postings at all was also considered a success.) -
When using journal format’s
includedirective, several kinds of error (read failure, cyclic include..) could show an off-by-one line number or excerpt, confusingly. This has been fixed. Also, attempting to include a rules file now gives a better error message. -
In CSV
ifrules, match group references like\1no longer get confused by differing case. (Jay Neubrand, #2419) -
add,commodities, anddiffnow support the –conf and -n/–no-conf flags, like other commands. #2446 -
On Windows machines, the
addcommand now properly shows green prompts instead of ANSI codes. #2410 -
Balance reports now properly show the historical balance even when the report period is empty. #2403
-
Balance reports’ csv output, and the
balance --budgetreport, now respect the –summary-only flag. (Stephen Morgan, #2411, #2443) -
The
democommand no longer mentions-- ASCIINEMAOPTSin help. Also it shows a better error message when asciinema is not installed. -
hledger help -m TOPICorhledger help -i TOPICnow show the help for TOPIC, as intended. #2399 -
Since hledger 1.32.1, the
importcommand, when importing multiple files at once, would write an empty .latest file for data files with no new transactions (causing all transactions in those data files to appear new on next import). This is now fixed. #2444
Features
-
CSV rules files can now run a shell command to clean the data:
# read the latest foo*.csv file, and replace "USD" with "$" source foo*.csv | sed -e 's/USD/$/g'or to generate the data:
# fetch JSON from simplefin.org, then transform it to CSV source | simplefinjson | simplefincsvWhenever hledger runs one of these commands, it will echo the command on stderr.
-
The
importcommand can now automatically archive imported CSV data files, saving a dated copy in adata/directory. This can be useful for troubleshooting, or for regenerating entries later with improved rules. To enable it, addarchiveto the rules file.This and the previous feature can simplify file management and reduce the need for support scripts.
Improvements
-
In command line help, flag group headings have been simplified. And the help for -f/–file,
add, andimportis now clearer. -
When given both an unknown command and an unknown flag, hledger now gives a clearer error message (about the command, not the flag). #2388
-
A long standing awkwardness with addon commands has been solved: you can now use addon options freely in a hledger command line; you don’t need to write a
--argument first. #458 -
In smart dates and period expressions, quarter syntax like
2025q1orQ2is now fully supported. -
In end-value reports where the end date is unspecified, market prices in the future can no longer influence the report end date and valuation date. (Market prices on or before today, still can.) #2445
-
A
tag:query with theaccountscommand now only matches account tags, not posting tags. Eg,hledger accounts tag:tnow lists only account a from this journal:account a ; t: 2025-01-01 a 1 b -1 ; t: -
Journal format’s
includedirective now has more robust and convenient glob patterns:**can match both directories and filenames**now automatically ignores anything under dotted directories, like .git/, foo/.secret/, etc. (If you do want it to search dotted directories, you can use the –old-glob flag for now to restore the old behaviour. See also Glob#49.)- Glob patterns with wildcards now automatically exclude the current file.
Eg
include **.journalwill include all other .journal files in this directory and below.
-
include’s error messages and debug messages have been improved. Eg, the including file paths are also shown. -
Journal format’s auto posting rules can now use
%accountto insert the account name from the matched posting. (Stephen Morgan, #1975, #2412) -
The
aregistercommand no longer abbreviates account names when producingcsv,html, orfodsoutput. (savanto, #1995, #2416) -
The
commodities,payeesandtagscommands now have –used/–declared/–undeclared/–unused flags, likeaccounts. And there has been a general cleanup of options and help across these four commands. -
The
setupcommand’s output has been improved. Lack of a pager is now reported as info, not warning (there’s no default pager on Windows). Shell completions are ignored for now.
Docs
- add: clarify that add is for journal format only
- addon commands: edits, drop
--argument from all examples #458 - areg: clarification
- bin: README updates
- COMMANDS: mention general options
- completions: README updates
- config files: no longer experimental
- csv: date-format: mention lack of support for local time formats #1874
- csv: source, archive: rewrite, add examples
- Depth: fix typo
- github release docs: simplify install commands
- import: use windows-compatible quotes in watchexec example
- include directive: update docs; clarify effect, glob limitations
- note fish LEDGER_FILE setup
- options: mention that flag+value can’t combine with other flags #2059
- print: improve –location help
- smart dates: fix typo
Examples
- CSV rules for Eternl cryptocurrency wallet
- VAT example
Scripts/addons
- renamed paypaljson2csv to paypaljson
- simplefinjson, simplefincsv: new helpers for downloading/converting data from simplefin.org bank aggregator
hledger-ui 1.50
Breaking changes
- hledger now requires at least GHC 9.6 (and base 4.18), to ease maintenance.
Improvements
- Use hledger 1.50
hledger-web 1.50
Breaking changes
- hledger now requires at least GHC 9.6 (and base 4.18), to ease maintenance.
Fixes
-
The register chart is no longer hidden when the window is narrow.
-
Dragging on the register chart now selects date ranges more accurately. Eg, now you can select a range including transactions at the rightmost edge of the chart.
Improvements
- Use hledger 1.50
project changes 1.50
Doc updates
- FINANCE
- ISSUES
- REGRESSIONS
- RELEASING
- SCHEDULE &
just schedulescript
Website
- fix the “edit this page” link
- redirects: handle more old pages; fix some old redirects to #FMT-format; stop redirecting /timeclock, /timedot, /timedot.html
- set up a github issue template clarifying this repo’s scope
- shortcut urls: release.hledger.org, nightly.hledger.org, regressions.hledger.org; readyprs.hledger.org excludes PRs with needs-* labels
- sidebar: reorganise
- sidebar: leave all links visible to avoid popping
- sidebar: avoid duplicate links, they’re no longer allowed
- sidebar: link to the current release’s manuals, not the dev version’s
- Beancount: edits, new conversion tips
- Docs: consolidate all user docs onto a single Docs page
- Editors: more vs code extensions, more emacs calc notes
- Export: updates
- FAQ: updates
- Hledger By Example: start a new progressive “book”, with 18 pages
- Tutorial: hledger basics -> to Tutorial: hledger add
- Home: new, shorter home page content
- Home: show a quote/testimonial, updating on the hour
- Investments: fix link (#2436)
- Invoicing: fix kairos link
- Ledger: edits
Infrastructure/Misc
- hledger now requires at least GHC 9.6 (and base 4.18), to ease maintenance.
- bump default build, tools to lts 24.8 / ghc 9.10.2
- docker: sync Docker GHC version with Stack configuration, update dependencies, and replace deprecated Dockerfile syntax. (Lukas Fleischer)
- github workflows improvements
- tool updates: checkembeddedfiles, devtag, nightly-push, nightlytag, relver, test
- update github issue templates
credits 1.50
Simon Michael (@simonmichael), Stephen Morgan (@Xitian9), Jay Neubrand (@jneubrand), Lukas Fleischer (@lfos), savanto (@savanto).
2025-06-13 hledger-1.43.2
hledger 1.43.2
-
hledger no longer shows an error message or exits with error status when its output is truncated in a piped command. (This broke in 1.43.) #2405
-
The
addcommand’s doc now describes how it interacts with balance assertions and balance assignments. #2406 -
aregisternow consistently rounds amounts to display precision again. (This broke in 1.32.) #2407 -
Changelog, release notes: mention improved decoding errors #73; add missing issue numbers
hledger-ui 1.43.2
- Use hledger-1.43.2
hledger-web 1.43.2
-
Use hledger-1.43.2
-
Add missing issue numbers in the changelog
credits 1.43.2
Simon Michael.
2025-06-04 hledger-1.43.1
hledger 1.43.1
Fixes
cur:in posting-based reports likebalanceandregisteronce again filters multicommodity amounts as it should. #2396- More error messages were made consistent, hiding call stack etc. #2367
Docs
hledger-ui 1.43.1
-
More error messages were made consistent, hiding call stack etc. #2367
-
Allow brick 2.9
hledger-web 1.43.1
Fixes
- Fixed a compilation error when not in the hledger source tree. #2397
project changes 1.43.1
- tools:
- Shake changelogs: fix missing blank lines around old heading
- ghcruns-download: make tmp dir
- docs: RELEASING
credits 1.43.1
Simon Michael.
2025-06-01 hledger-1.43
hledger 1.43
new setup command, better boolean queries, add assertions, timeclock concurrent sessions, CSV if rules debug output
Breaking changes
-
Timeclock format now supports multiple simultaneous clocked-in sessions. A clockout entry can reference the account name of the clockin it is ending, otherwise it will end the most recent clockin.
This is a potentially breaking change, because we previously ignored the description on clockout entries. For now, you can restore the old behaviour by adding the
--timeclock-oldflag. This may be removed later, so if you needed it, please let us know! (Michael Rees, #2141)
Fixes
-
Some boolean queries involving
amt:orcur:did not match correctly; now they do. (Simon Michael, Stephen Morgan, #2371, #2373, #2387) -
The
roicommand has fixed a bug with PnL attributed to the wrong period. (Dmitry Astapov, #2391) -
For a long time, hledger docs stated that hledger’s input and output had to be UTF-8 encoded. In fact this was wrong; hledger has always used the system locale’s encoding for both. Docs like Install: Text encoding and hledger: Text encoding have been updated. #2394
-
CLI error messages now have consistent format independent of the GHC version hledger was compiled with. #2367
Features
-
A new
setupcommand checks your hledger installation and shows information useful for troubleshooting. It will do more in future. For now,setupshould be reliable and accurate; please test it on all your machines and share the results with us. Experimental.When you run this command, hledger attempts one HTTP request, with a 10s timeout, to hledger.org, to detect the current release version.
-
New
any:"BOOLEANQUERY"andall:"BOOLEANQUERY"queries can be used with transaction-matching commands likeprintandimport.Unlike
expr:"BOOLEANQUERY", where the parts of the boolean query are allowed to match different postings in the transaction, withany:andall:the entire boolean query must match a single posting.any:matches the transaction if any posting is matched,all:matches the transaction if all postings are matched. -
The
addcommand now allows balance assertions to be added, after an amount. Also, whenever you enter an amount, it now checks the journal’s balance assertions and will not allow adding an amount that would cause assertion failure (unless you use the-I/--ignore-assertionsflag). (Michael Rees, #2355, #2356) -
The hledger-print-location script, which shows transactions’ file positions, is now built in to
printas the--locationflag. (Sam Salmahri, #2368)
Improvements
-
Core data types are now more strict, which in some cases reduces memory and time usage by up to 15%. (Stephen Morgan, #2381, #2385, #2387)
-
Config files (and maybe timedot files) no longer require a final newline.
-
In journal format,
includedirectives can now have same-line comments. -
CSV
ifrules now produce more--debug=2output, useful for troubleshooting. -
CSV debug output now displays records as the
ifmatchers see them. (Comma separated and with quotes and outer whitespace removed.) -
A number of error messages have been updated to show a consistent format. (reading a nonexistent data file, reading an unsafe dotted file name on Windows, demo not found, demo asciinema error, diff bad arguments, print –match with no match, register –match with no match, roi with no investment transactions). #2367
-
When unicode/non-ascii text can’t be decoded by the system locale’s text encoding, we now show a consistent informative error message explaining it. #73
-
Support GHC 9.12.
Docs
- Account types: rewrite
- Assertions and multiple files: edits
- Balance assertions and multiple files
- balance: edits
- Beancount output: edits
- check: edits
- Config file: drop the s
- CSV: clarify that CSV means delimiter-separated values, link to wikipedia DSV page
- CSV:description field: fix link hledger_site#133
- Fix broken hledger-iadd link
- Fix various broken “Commodity display style” links (JB Rainsberger, Simon Michael, [hledger_site/#132])
- How CSV rules are evaluated: clarify
- Journal: cheatsheet edits
- print: simplify #2368
- Queries: discuss transaction vs posting queries; cleanups
- Queries: fix the explanation of boolean queries and space-separated terms
Examples
- examples/csv/nps.csv.sh updated (Pranesh Prakash)
- examples/forecast-budget* -> examples/goal-budget*
- examples/i18n/*.journal: top level account declarations in several languages.
- examples/quickstart.journal: the quickstart journal from the home page
Scripts/addons
- bin/balance-as-budget-multi.hs (Dmitry Astapov)
- bin/bashrc: command alias updates
- bin/hledger-sankeymatic
API
- Hledger.Cli.Conf: make getConf total, add getConf’
- Hledger.Cli.Version: add more version helpers, rename HledgerBinaryInfo
hledger-ui 1.43
Fixes
- Require fsnotify >=0.4.2.0, which fixes some events being ignored on mac, possibly making hledger-ui –watch more reliable in this regard.
Improvements
-
CLI error messages now have consistent clean format independent of GHC version. #2367
-
Support GHC 9.12.
Docs
- Update –watch docs.
- Drop obsolete mention of Windows non-support.
hledger-web 1.43
Features
- Serve openapi.json, documenting the hledger-web HTTP API so that tools like open-webui and LLMs can query hledger-web for context. (Ben Sima)
Improvements
-
The search help popup has been updated, and now shows the hledger-web version.
-
The default “serve and browse” mode now has an explicit
--serve-browsemode flag, for consistency. -
The old
--serverflag is now deprecated and hidden. Use--serveinstead. -
CLI error messages now have consistent clean format independent of GHC version. #2367
-
Fix capitalisation in Sandstorm app metadata.
-
Support GHC 9.12.
-
Drop base-compat dependency (Thomas Miedema)
Docs
- Fix outdated PERMISSIONS doc.
project changes 1.43
Docs
- Updated: FILES, FINANCE, ISSUES, PULLREQUESTS, REGRESSIONS, RELEASING, ghrelnotes, relnotes
Infrastructure/Misc
- issue labels updates,
severity*->annoyance*,impact*->affects* - new shortcut urls: assignedprs.hledger.org, unassignedprs.hledger.org
- clean up LICENSE files, main module licensing headers, add AUTHORS.md, try to fix github license detection
- cabal files now specify GPLv3+ exactly. #2359
- building the hledger packages now requires at least Cabal 2.2+ or stack built with Cabal 2.2+ (was 1.12+).
- avoid a ghci warning if :rmain is defined in user’s config.
- simplify stack yaml file names
- workflows updates:
- binaries*: simplify, cleanup
- binaries-linux-x64: install ghcup only if not cached; fix cache
- windows: use preinstalled stack
- docker: draft of a docker image build/publish workflow (Doug Goldstein, #2002)
- nightly: adds latest binaries to the “nightly” release
- tools updated: binaries, devtag-push, ghnightly, ghnightly-bin, ghnightlynotes-push, ghrel, ghrelease-upload, ghrelnotes-push, ghrun, ghrun-id, ghrun-open, ghruns, ghruns-download, ghruns-open, hackageupload, nightly, nightlybin, nightlytag
credits 1.43
Simon Michael (@simonmichael), Michael Rees (@reesmichael1), Thomas Miedema (@thomie), Dmitry Astapov (@adept), Pranesh Prakash (@the-solipsist), Stephen Morgan (@Xitian9), Sam Almahri (@samahri), Ben Sima (@bsima), Doug Goldstein (@cardoe).
2025-05-16 hledger-1.42.2
hledger 1.42.2
Fixes
-
The test command can pass options to tasty again (this broke in 1.42). Also, any arguments before – are now passed to tasty as test-selecting -p options. #2386
-
Require extra >= 1.7.11, fixing the stack8.10.yaml build. (Thomas Miedema)
Docs
- CSV encoding: fix inaccurate list of encoding names. #2354
- Fix broken links to “Commodity display style”. (Simon Michael, J.B. Rainsberger hledger_site#132)
- Misc. edits/fixes: beancount output, commands list, balance command, hledger-iadd link, csv tutorial link
hledger-ui 1.42.2
Fixes
-
Require fsnotify-0.4.2.0+/hfsevents-0.1.8+, which fixes some events being ignored on mac (see https://github.com/luite/hfsevents/pull/19), which should help
hledger-ui --watcha little. -
Require extra >= 1.7.11, fixing the stack8.10.yaml build. (Thomas Miedema)
Docs
- Update –watch notes
- Drop obsolete Windows non-support note
hledger-web 1.42.2
Fixes
-
Don’t hang when saving a large file (this broke in 1.42). #2389
-
Require extra >= 1.7.11, fixing the stack8.10.yaml build. (Thomas Miedema)
project changes 1.42.2
Docs
- relnotes: fix links to hackage changelogs
credits 1.42.2
Simon Michael (@simonmichael), Thomas Miedema (@thomie).
2025-03-12 hledger-1.42.1
hledger 1.42.1
Fixes
-
Fix a regression in the parsing of single ampersands (&) in csv rules. (Thomas Miedema, #2352)
-
The run and repl commands no longer cache data wrongly when using the –forecast option. (Dmitry Astapov, #2345)
-
The run and repl commands no longer miss a caching opportunity when using –forecast with a period argument. (Stephen Morgan)
-
run, repl: doc fixes (Pranesh Prakash, Simon Michael)
hledger-ui 1.42.1
- Allow vty 6.4.
hledger-web 1.42.1
project changes 1.42.1
Infrastructure/Misc
-
Tools updates
-
Fix cabalfilestest: test all cabal files before any hackage upload.
credits 1.42.1
Simon Michael (@simonmichael), Dmitry Astapov (@adept), Pranesh Prakash (@the-solipsist), Stephen Morgan (@Xitian9), Thomas Miedema (@thomie).
2025-03-07 hledger-1.42
hledger 1.42
run & repl commands, non-UTF8 CSV, same-line if matchers, more pivot targets, fixes.
Fixes
-
hledger’s default options for the
lesspager no longer include –use-color, which caused older less versions (eg 551) to break. #2335 -
In balance –budget reports, costs no longer prevent display of percentages. #2327
-
In the balance command’s HTML output, -H/–historical now suppresses the total heading.
-
print –help now shows the correct default for –round. #2318
-
close –infer-costs no longer implies the -x/–explicit flag. #1826
-
add: Account names provided on the command line are no longer ignored. #2305
-
bs/bse/cf/is no longer show the unsupported –budget option in their help. #2302
-
The print command now ignores –depth entirely. Previously, a depth limit caused it to show only transactions referencing accounts as deep or deeper than that.
-
Week periods beginning in the previous year are now shown correctly. Eg the week beginning 2024-12-30 (which is week 1 of 2025 because the thursday falls in 2025) was previously shown as 2024-W01, and is now shown as 2025-W01. #2304
Features
-
runandreplare new commands which run multiple commands without re-parsing data files, so they run faster.runruns a sequence of commands from files or command line arguments, andreplprovides an interactive prompt with readline-style history. (Dmitry Astapov, Simon Michael, #2323, #2328) -
commandsis a new explicit command for showing the commands list; it’s useful in the REPL. With –builtin, it shows only the builtin commands. -
hledger can now read CSV/SSV/TSV data in encodings other than UTF8, using the new
encodingCSV rule. (Joschua Kesper, #2319) -
ifmatchers in CSV rules can now be combined on the same line, separated by&&(AND) or&& !(AND NOT). This makesiftables more expressive. Examples:if %description amazon && %date 2025-02-22 account2 expenses:books if,account2 %description amazon && %date 2025-02-22, expenses:booksThe next-line
&operator also may now be written as&&, for consistency. (Thomas Miedema, #2333)
Improvements
-
Terminal width is now detected more robustly, using the POSIX API. This means that register commands will more reliably use the proper terminal width, eg when $TERM or $COLUMNS do not have a correct value, and on Windows. hledger no longer uses $COLUMNS. (gesh, #2332, #2340)
-
Error messages (first line) in the terminal are now shown in red (and bold), and warning messages are shown in yellow, when ANSI codes are supported and permitted.
-
–pivot can now also pivot on amount quantity (
amt), amount cost (cost), and/or commodity symbol (commorcur). -
The close command’s –migrate flag has been renamed to
--clopen. The start: tag has been renamed toclopen:, and its default value now excludes the new file’s extension. And close –assign’s tag has been renamed toassign:. -
The print command now supports the –invert flag. #2314
-
The roi command is now faster: it no longer checks every day with P directive, and the “one period per report interval” case has been optimised. (Dmitry Astapov)
-
Two more file extensions are now recognised as possible hledger addon commands:
.oshand.ysh.
Docs
- aregister: Drop an inconsistent newline from options help.
- balance: improve –layout option help.
- close: doc rewrites
- shell completions: mention zsh; cleanups
- cost/lot notations: clarify
- cost, value reporting: edits
- Directive effects: fix account types link #126
- commodity styles: fix typo [hledger_site#123]
- commodity directive: clarify
- close: mention the balance assertions
- pager: mention –pager=no
- Aliases and account types: better troubleshooting command
- Beancount output: mention limitations: P and balance assignments
- Balance report output: drop outdated note about –tree and HTML #1846
Scripts/addons
-
hledger-balance-as-budget properly applies commodity styles now. (Dmitry Astapov)
-
hledger-git now runs pass-through git commands in the right repo.
-
hledger-git now checks for a git repo more robustly. (Lars Kellogg-Stedman)
-
hledger-jj is another easy CLI for keeping hledger files in version control, using newer tech (jujutsu and oil shell’s ysh).
-
hledger-script-example.hs has had some cleanup.
hledger-ui 1.42
Fixes
- Startup arguments provided at the CLI are no longer passed to
addwhen pressing theakey. #2313
Improvements
- Allow vty 6.3.
- Allow brick 2.8.
hledger-web 1.42
Fixes
- Fix a test suite build issue: build it with -threaded.
project changes 1.42
Docs
- new/updated: ACHIEVEMENTS, CREDITS, FINANCE, MOCKUPS, REGRESSIONS, RELEASING, Scripts
- examples/csv: pooltool-rewards.csv.rule cleanup
- examples: some sample hledger run scripts
- examples: test files for CSV encoding #2319
Infrastructure/Misc
-
Unix bindists in github releases now include the man pages and info manuals. #2309
-
Unix bindists in github releases are now normal .tar.gz files, without the extra zip compression. [#2299]
-
Release notes and install instructions in github releases are now collapsed by default again. This makes the sequence of dates and releases clearer. Github automatically expands the assets of the latest release, so those will be visible by default. For a full text search of all release notes, use the https://hledger.org/relnotes.html page.
-
hledger-install: if installing third party packages fails, it now tries again with bounds relaxed. This makes hledger-iadd and hledger-interest more likely to install successfully, even if their bounds have not yet been updated for a new hledger release.
-
just functest now fails if there are warnings. (It does not force recompilation of already compiled modules, but even so this should help catch more warnings before pushing them to CI.)
-
new/changed recipes/tools: bench*.sh, changelogs-catchup, changelogs-finalise, changelogs-reset, devtag, ghrelnotes, ghrelnotes-publish, log-headtail, log-push, log-save, modulediag, modulediags, modulediags-view, packagediags, packagediags-view, releasediag, relnotes relprep -> relbranch, reltags, reltags-push, showrelnotes, site-manuals-snapshot, site-watch, time, tldr-diff
-
gitignore cleanups.
credits 1.42
Simon Michael (@simonmichael), Dmitry Astapov (@adept), gesh (@hseg), Thomas Miedema (@thomie), Joschua Kesper (@jokesper), Lars Kellogg-Stedman (@larks).
2024-12-09 hledger-1.41
Valuation fix, improved HTML and FODS output, multiple depths, terminal pagination, robust export to Beancount, …
hledger 1.41
Breaking changes
-
Accounts named “equity:conversion”, “equity:trading”, or “equity:trade(s)”, which are detected as type
V/Conversion, will now revert to typeE/Equityinstead if any other account has been declared as typeV/Conversion. -
When built with ghc 9.10.1, hledger shows two extra newlines after any error message.
Fixes
-
A somewhat severe, though hopefully rare, valuation bug has been fixed. In certain circumstances, values could be calculated inaccurately, because of display-rounding occurring inappropriately during calculations. #2254
Specifically: when there was no direct P price for the target commodity, so that hledger had to convert via a chain of prices, and if all of those price amounts had too few decimal places, then the result could be inaccurate. An example:
P 2000-01-01 A 10.5 B P 2000-01-01 B 100.5 C 2000-01-01 (a) 100 A $ hledger-1.40 print -X C 2000-01-01 (a) 105520 C ; wrong $ hledger-1.41 print -X C 2000-01-01 (a) 105525 C ; right -
bs/bse/cf/is’s HTML output no longer includes excess heading cells, andbs/bse’s HTML output no longer shows an inappropriate Total heading with-T. (balancesheetdoes not support-T.) (Henning Thielemann) -
Balance commands’ HTML, CSV and FODS output now show tree mode properly indented (using no-break spaces). (Henning Thielemann)
-
In the HTML output of
bs/bse/cf/isreports, Net amounts in the Net row are now formatted like the others. (Bas van Dijk) -
In
bs/bse/cf/isHTML output, some unnecessary TH cells have been fixed. #2225 (Henning Thielemann) -
The
printcommand now ignores a depth limit entirely. Previously, a depth limit caused it to show only transactions referencing accounts as deep or deeper than that. -
In the
roicommand, a division by zero error (when all assets were sold) has been fixed. #2281 (Dmitry Astapov) -
In a multi-line comment generated by CSV rules, tags on all lines now work (ie, can be matched). Posting dates in comments generated from CSV also now work. #2241
-
hledger’s bash shell completions are now up to date with the latest CLI. #986
-
When showing output with a pager, if
$PAGERis set to something not found in PATH, we now ignore it instead of raising an error. -
hledger --color=yes | less -Rnow shows bold headings as you’d expect.
Features
-
The
print,registerandaregistercommands now support HTML and FODS output, and thebs/bse/cf/iscommands now support FODS output. This means all of the “STANDARD REPORTS” commands, and thebalancecommand, now support text, HTML, CSV, TSV, or FODS output. (Henning Thielemann) -
When generating HTML output with the register or balance commands, the
--base-urloption will add hyperlinks to hledger-web, allowing you to view the detailed transactions if you have hledger-web running. (Henning Thielemann) -
Reports can now specify different display depths for certain accounts, rather than showing all accounts with the same depth limit. Multiple
--depth=ACCTREGEX=DEPTHoptions (ordepth:ACCTREGEX=DEPTHarguments can be used. For example, this will clip all accounts matching “assets” to depth 3, all accounts matching “expenses” to depth 2, and all other accounts to depth 1:--depth assets=3 --depth expenses=2 --depth 1(Stephen Morgan, #2292) -
In unix-like environments, hledger now uses a pager (
$PAGER,less, ormore) for all large terminal output, not just for help. You can override this with the new--pageroption. The pager is expected to handle hledger’s ANSI colour output (unless you disable that). Iflessis used, it will be configured automatically, or you can override this by setting options in aHLEDGER_LESSenvironment variable. -
The
printcommand’sbeancountoutput is now much more Beancount-compatible #2295. Other than using--aliasto provide the top-level account names Beancount requires, you should rarely have to do anything special to produce a journal thatbean-checkaccepts. hledger will automatically adjust problematic names, encode unsupported characters, and so on. See hledger: Beancount output for the full details.This supersedes the
ledger2beancounttool, and makes using Beancount tools, especially Fava, practical for hledger users. In many cases this should just work:hledger [ALIASES] print -o tmp.beancount; fava tmp.beancount
Improvements
-
ifblocks in CSV rules now allow& !(AND NOT) on the same line. -
When reading
.latestfiles, whitespace is now ignored, and any date parse failure is reported with the file and line number. -
In journal format, P directives now require a space after the first symbol, preventing surprises like
P 2024-10-31 a0 1parsed asP 2024-10-31 a 01. #2280 -
aregisterhas a new--heading=YNoption, for disabling the report heading. (Henning Thielemann) -
aregisternow supports the--invertand--cumulativeflags, like theregistercommand. (Henning Thielemann) -
The balance commands’ HTML and FODS output now shows table borders consistently. (Henning Thielemann)
-
In the balance commands’ HTML output, row headings now span multiple rows when appropriate, rather than being repeated. (Henning Thielemann)
-
Balance commands now support
--transposewhen generating HTML output. (Henning Thielemann) -
The
balancecommand’s--layout=tidynow affects HTML and FODS output at least to some extent (not just CSV output). And it always disables the totals row. (Henning Thielemann) -
The
balancecommand’s FODS output now picks a report title based on the report mode: “Balance Report”, “Multi-period Balance Report”, or “Budget Report”. (Henning Thielemann) -
balanceandaregister’s HTML output will now use a hledger.css file if present, likebs/bse/cf/is. -
bs/bse/cf/isnow support the--count(postings count) report type, likebalance. -
The balance commands’ options help has had some cleanup.
-
The error messages from
check accountsandcheck recentassertionsare now clearer. -
The
check commoditiescommand now also checks commodities used in P directives. #2280 -
The
commoditiescommand now also list commodities mentioned in P directives. #2280 -
All of hledger’s internal hidden (but searchable) tags can now be made visible by
print --verbose-tags, which is useful for troubleshooting--infer-equity,--infer-costs, and the detection of redundant costs and conversion postings. Also,- Some hidden tags have been renamed for clarity:
_modifiedis now_modified-transaction,_cost-matchedis now_cost-posting,_conversion-matchedis now_conversion-posting. - The
generated-posting:tag added by--infer-equityis now valueless. - The
modified-transaction:tag added by--autonow appears on its own line.
- Some hidden tags have been renamed for clarity:
-
Using the
-NUMshortcut (for--depth NUM) in a config file now works. -
Setting the
--coloroption in a config file now works (except it does not affect--debugoutput, currently). -
The
--coloroption’s suggested values are nowyes/y,no/n, orauto/a.alwaysandneverare no longer documented, though still supported. -
More compact and informative
--debug=2output during valuation. Market prices are now shown using one line each, the known prices are listed, and the status of--infer-market-pricesis shown. #2287 -
More informative
--debug=7output from CSV rules. -
The hledger packages have a new
debugbuild flag. Builds made with ghc 9.10+ and this flag will show some kind of stack trace if the program exits with an error. (These will improve in future ghc versions.) -
Disabled the unused
ghcdebugbuild flag and ghc-debug support, for now. -
Allow megaparsec 9.7.
-
ghc 9.10 / base 4.20 are now supported.
Docs
- Windows: added tips for setting
LEDGER_FILE(Amadeusz Wieczorek, hledger_site#119) - csv: if blocks: explain matchers and field names better #2289
- import: rewrite; rename “date skipping” to “overlap detection”
- import: added tips for first import
- assertions: mention their behaviour with posting status
- journal: rewrite the Tags section
- pager: note that
help -p TOPICuses less; link to less FAQ - query types: updates, add headings, mention tag:’s infix matching
- Regular expressions: note possible RTL/bidi limitation (?)
- Special characters: rewrite, more precision, mention some Windows differences
- Output formats: expand, document beancount and FODS output
- Text output: expand/consolidate terminal topics
- FODS output: describe the advantages over CSV (Henning Thielemann)
- Debug output: note that the –debug option doesn’t work in config files.
- bal: improve –layout doc
- bal: also mention hledger.css and text encoding in balance doc
- html: note safari text encoding issue
- timedot: mention the common journal+timedot file setup #2238
- Install, manual: new shell completions doc. #986
- Config files: rewrite #2231
- examples/csv: an example of YNAB 4 data, and RTL text, with a workaround
- examples: hledger2beancount.conf
Scripts/addons
- bin/*: remove obsolete _FLAGS markers from –help
- examples/csv/csv-hledger-1.py: a python-based CSV converter script
API
- Hledger.Utils.IO’s ansi color helpers now respect the –color option.
- Hledger.Utils.IO.rgb’ now takes Float arguments instead of Word8.
- Hledger.Cli.Commands.Balance: export budgetReportAs* functions, for use in scripts. (Dmitry Astapov)
hledger-ui 1.41
Breaking changes
- When built with ghc 9.10.1, error messages are displayed with two extra trailing newlines.
Fixes
-
V (value) and C (cost) toggle keys once again reset each other as they should (broken since 1.21). (Gal Lakovnik Gorenec, #2284)
-
Bash shell completions are now up to date. #986
Features
Improvements
-
Allow clipping depth to be configured per account (until adjusted in app, at least). (Stephen Morgan, #2292)
-
Added helix as a supported editor for the
ekey. (amano.kenji) -
Added –pager and –color options as in hledger, affecting command line help. Also –color=no forces use of the “terminal” theme.
-
Added a new
debugbuild flag. Builds made with ghc 9.10+ and this flag will show some kind of partial stack trace if the program exits with an error. These will improve in future ghc versions. -
Disabled the unused
ghcdebugbuild flag and ghc-debug support, for now. -
Allow megaparsec 9.7.
-
Allow brick 2.5, 2.6.
-
Avoid brick 2.3.2, which doesn’t build on windows.
-
ghc 9.10 / base 4.20 are now supported.
Docs
- Mention that period navigation uses standard periods #2293
- Install, manual: new shell completions doc. #986
hledger-web 1.41
Breaking changes
- When built with ghc 9.10.1, error messages are displayed with two extra trailing newlines.
Fixes
-
hledger-web register no longer shows blank From/To accounts (broken since 1.33). (Henning Thielemann, #2227
-
Autocompletions now work in newly created account fields. #2215
-
Bash shell completions are now up to date. #986
Features
Improvements
-
Added –pager and –color options as in hledger, affecting command line help.
-
Added a new
debugbuild flag. Builds made with ghc 9.10+ and this flag will show some kind of partial stack trace if the program exits with an error. These will improve in future ghc versions. -
Disabled the unused
ghcdebugbuild flag and ghc-debug support, for now. -
allow megaparsec 9.7
-
ghc 9.10 / base 4.20 are now supported.
Docs
- Install, manual: new shell completions doc. #986
project changes 1.41
Docs
- REGRESSIONS: new table format; updates.
- CODE: notes on the use of haddock #2222
- Simplify github bug report template
- Add man pages and info manuals to the release bindists on github
Scripts/addons
- Fixed build errors in all bin/ scripts. (Dmitry Astapov)
- hledger-install: fix installation of hledger-ui
Infrastructure/Misc
- GHC 9.10 is now the default for dev builds and most github workflows.
- Add consistent greppable summaries to all workflows; various other improvements
- Add bash shell completion script to the release bindists (#2223, gesh/hseg, Simon Michael)
- tools/gtree: -u shows untracked files, -I adds ignored files.
- just doctest: pass through doctest arg(s).
credits 1.41
Simon Michael (@simonmichael), Henning Thielemann (@thielema), Dmitry Astapov (@adept), Stephen Morgan (@Xitian9), Bas van Dijk (@basvandijk), Gal Lakovnik Gorenec, amano.kenji, gesh (@hseg).
2024-09-09 hledger-1.40
Config file support, sortable register, FODS output, prettier tables.
hledger 1.40
Fixes
- Account tags (and type declarations) declared in multiple files are now combined correctly. #2202
- Several kinds of report interval now choose a better start date:
every Nth day of month from DATEwith periodic transactions #2218every M/D from DATEevery Nth WEEKDAY from DATE
- The balance commands’ html output no longer repeats the “Total” and “Net” headings when the totals row has multiple lines. And the layout has been improved and made more consistent with the text output.
- The
--tldrflag now also works with thetealdeertldr client.
Features
- You can now save command line options in a config file, to be added to your hledger commands either on demand or automatically. (This supersedes the older arguments files feature.) This has been a popular feature request. It has pros and cons, and is experimental; your testing and feedback is welcome. It changes the nature of hledger somewhat, which I have marked by giving this release a more memorable version number (1.40).
- The balance commands can now output in FODS format, a spreadsheet file format accepted by LibreOffice Calc. If you use LibreOffice this is better than CSV because it works across locales (decimal point vs. decimal comma, character encoding stored in XML header), and it supports fixed header rows and columns, cell types (string vs. number vs. date), separation of number and currency, styles (bold), and borders. You can still extract CSV from FODS/ODS with the ods2csv utility from Hackage. (Henning Thielemann)
- The
registerreport can now be sorted by date, description, account, amount, absolute amount, or a combination of these. (Michael Rees, #2211)
Improvements
- Command line processing has been overhauled and should be more robust in certain cases, with tweaked error messages and debug output. Command-specific flags can now optionally appear before the command name. (Though writing them afterward is usually more readable. Addon-specific flags must still come last, after
--.) - The
--rules-fileoption has been renamed to--rules. The old spelling is still supported as a hidden option. - Weekly reports’ week headings are now more compact, especially in single-year balance reports. (#2204, Victor Mihalache)
- The
balancecommand with no report interval, and alsobalance --budget, now support html output. (Henning Thielemann) - In balance commands’ html and csv output, “Total:” and “Net:” headings are now capitalised consistently.
bs/cf/isreports now show the report interval in their title.- The balance commands’ text output with the
--prettyflag now shows an outer table border and inter-column borders. - The
check recentassertionserror message is now more readable. - Timedot format now allows comment lines to be indented.
- When running the
tldr-node-clientclient, auto-update of the tldr database is now suppressed. - When running a tldr client fails, the warning now mentions the required
--renderflag. #2201 - The error message for unsupported regular expressions like
(?:foo)has been improved. --debughas moved to “General help flags”, making it available in more situations.- Some verbose debug output from command line processing has been demoted to level 2.
- Parsing timedot files now gives debug output at level 9.
- Allow doclayout 0.5.
Docs
- The hledger/hledger-ui/hledger-web manuals now list all command options as shown by
--help. - Added an example config file,
hledger.conf.sample. - The
diffandpricescommands’ help layout has been improved. add’s doc described the effect ofDwrongly, now fixed.- Date adjustments: rewrites and corrections
- Period headings: added
- Input: clarify that multiple -f options are allowed
- Scripts and add-ons: edits, list add-ons again
- Timeclock: edits, fix
ti/toscripts - Fixed “hledger and Ledger” links [hledger_site#112]
- examples/csv: Monzo CSV rules added
- examples/csv: Tiller CSV rules added
- examples/csv: Nordea CSV rules added (Arto Jonsson)
Scripts/addons
bin/bashrcupdates; add years, eachyear scriptsbin/hledger-simplebal: ignore config filesbin/hledger-script-example: explain shebang commands betterbin/hledger-register-max: update/fix
hledger-ui 1.40
Improvements
- The menu screen now supports the shift arrow and shift T keys, and its header shows any narrowed time period in effect, like other screens.
- Support brick 2.4.
Docs
- The description of the shift-T key (set period to today) has been fixed.
- The shift arrow keys and period narrowing have been clarified
hledger-web 1.40
Improvements
- We now guess a more robust base url when
--base-urlis not specified. Now relative links to js/css resources will use the same hostname etc. that the main page was requested from, making them work better when accessed via multiple IP addresses/hostnames without an explicit--base-urlsetting. (A followup to #2099, #2100 and #2127.) - We now require a http[s] scheme in
--base-url’s value. Previously it accepted just a hostname, and generated bad links.
project changes 1.40
Docs
- In the hledger 1.29 release notes, Date adjustments has had some corrections.
- Github release notes template cleanups; fix mac, linux install commands.
- README: fixed contributors link.
- RELEASING: updates
Scripts/addons
- hledger-install: cleanups, bump versions, perhaps fix hledger-interest install
- hledger-install: clarify some stack/cabal setup messages
Infrastructure/Misc
- Shake.hs: fix partial warnings
- Shake cmdhelp: renamed to cmddocs, and it now also updates the options listed in the manuals, and shows progress output. It should be run (at some point) after changing commands’ docs or options.
- Shake txtmanuals: silence all but wide table warnings
- just file cleanups; update to support just 1.28+
- just twih: date fixes
- just ghci: -fobject-code was a mistake, keep everything interpreted
- just functest: try again to reduce rebuilding/slowdowns when testing
- just installrel: update for .tar.gz
- ci scripts: cleanup, fix a macos-ism
credits 1.40
Simon Michael (@simonmichael), Henning Thielemann (@thielema), Michael Rees (@reesmichael1), Arto Jonsson (@artoj), Victor Mihalache (@victormihalache).
2024-06-01 hledger-1.34
hledger 1.34
–tldr (short command examples), reorganised commands list, ghc-debug support
Breaking changes
check ordereddatesno longer supports--date2. Also (not a breaking change):--date2and secondary dates are now officially deprecated in hledger, though kept for compatibility.
Features
-
You can now get a quick list of example command lines for hledger or its most useful subcommands by adding the
--tldrflag (or just--tl). For best appearance you should install thetldrclient, though it’s not required.These short “tldr pages” are a great counterbalance to verbose PTA docs. You can also use
tldrwithout hledger to view the latest versions, or translations:tldr hledger[-COMMAND]. Or you can browse tldr pages online. Consider contributing translations! More tips at https://github.com/simonmichael/hledger/tree/master/doc/tldr.
Improvements
-
The
hledgercommands list has been reorganised, with commands listed roughly in the order you’ll need them. -
The general flags descriptions in
--helphave been updated and grouped. -
Correctness checks now run in a documented order.
commoditiesare now checked beforeaccounts, andtagsbeforerecentassertions. When bothordereddatesandassertionschecks are enabled,ordereddatesnow runs first, giving more useful error messages. -
-I/--ignore-assertionsis now overridden by-s/--strict(orcheck assertions), enabling more flexible workflows. Eg you canalias hl="hledger -I"to delay balance assertions checking until you add-sto commands. -
--colorand--prettynow also acceptyornas argument. -
When built with the
ghcdebugflag and started with--debug=-1, hledger can be controlled by ghc-debug clients like ghc-debug-brick or a ghc-debug query script, for analysing memory/profile info.
Fixes
-
hledger COMMAND --manandhledger help TOPIC --mannow properly scroll the man page to the TOPIC or COMMAND heading. The exact/prefix matching behaviour has been clarified inhelp --help. -
In journal files,
includedirectives with trailing whitespace are now parsed correctly. -
The help command’s help flags are now consistent with other commands (and it has
--debugas a hidden flag). -
Build errors with GHC 8.10 have been fixed. #2198
Docs
- The tables of contents on hledger.org pages now just list top-level headings, (and the hledger manual structure has been adjusted for this). This makes the hledger manual on hledger.org more scannable and less scary.
- add: drop lengthy transcript, add simpler example commands (from tldr)
- Amount formatting: move down, it’s not the best first topic
- balance: mention the
--summary-onlyflag - check: expand check descriptions
- examples: CSV rules: vanguard, fidelity, paypal updates
- Generating data: rewrite
- JSON output: link to OpenAPI spec
- manuals: synopsis, options cleanup/consistency
- Options: correction, NO_COLOR does not override –color
- PART 4: COMMANDS: reorganise into groups, like the CLI commands list.
- Period expressions: mention last day of month adjusting #2005
- Secondary dates: expand, and declare them deprecated.
- Time periods cleanup, simplify markup
- Unicode characters: mention UTF-8 on windows
Scripts/addons
- Added
hledger-pricehist, an alias for thepricehistmarket price fetcher so that it can appear in hledger’s commands list.
hledger-ui 1.34
Features
- You can now get a quick list of example command lines by running with
--tldr(or just--tl). For best appearance, install thetldrclient, though it’s not required.
Improvements
-
The general flags in
--helphave been updated and grouped, consistent with hledger. -
When built with the
ghcdebugflag and started with--debug=-1, hledger-ui can be controlled by ghc-debug clients like ghc-debug-brick or a ghc-debug query script, for analysing memory/profile info.
hledger-web 1.34
Features
- You can now get a quick list of example command lines by running with
--tldr(or just--tl). For best appearance, install thetldrclient, though it’s not required.
Improvements
-
The general flags in
--helphave been updated and grouped, consistent with hledger. -
When built with the
ghcdebugflag and started with--debug=-1, hledger-web can be controlled by ghc-debug clients like ghc-debug-brick or a ghc-debug query script, for analysing memory/profile info.
Docs
- A basic OpenAPI specification is provided for hledger-web’s JSON-over-HTTP API. This is also applicable to
hledger print’s JSON output format.
project changes 1.34
Docs
- move release notes from the hledger_site repo to the main hledger repo
- github release notes: show the release notes, hide the install instructions by default
- github release notes: improve windows install commands
- github release notes: start mentioning github usernames, enabling the Contributors avatar list
- dev docs: new Developer FAQ, Contributor Quick Start updates
Scripts/addons
hledger-install.shnow uses stackage nightly, and a failure on non-Windows platforms has been fixed.
Infrastructure/misc
- A new
releaseworkflow creates github releases, uploads release binaries and generates release notes. - Github release binaries for mac and linux are now in .tar.gz format (no longer tarred and zipped).
- There is a new
oldestworkflow for testing the oldest GHC we support (currently 8.10.7). - The
binaries-mac-x64workflow has been bumped from GHC 9.4 to 9.8. - The master branch’s
ciworkflow has been updated to Ubuntu 24.04 and uses the preinstalled GHC & stack, saving some work. md-issue-refshelps generate markdown issue links.relnotes.hshelps generate release notes from changelogs.- The project
Makefilehas now been fully replaced byJustfile.
credits 1.34
Simon Michael (@simonmichael)
2024-05-02 hledger-1.33.1
hledger 1.33.1
-
process >=1.6.19.0 seems not strictly needed and is no longer required, improving installability. #2149
-
printandclosenow show a trailing decimal mark on cost amounts also, when needed to disambiguate a digit group mark. -
The balance commands’ HTML output now includes digit group marks when appropriate (fixes a regression in 1.25). #2196
-
The add command no longer shows ANSI escape codes in terminals that don’t support them.
-
Doc updates:
- import: Skipping -> Date skipping, discuss commodity styles more
- csv: Amount decimal places: expand, note import behaviour
hledger-ui 1.33.1
-
Require vty-windows-0.2.0.2+ to avoid display problems in recent MS Terminal on Windows.
-
process >=1.6.19.0 seems not strictly needed and is no longer required, improving installability. #2149
hledger-web 1.33.1
- Support base64 >=1.0
credits 1.33.1
- Simon Michael (@simonnmichael)
2024-04-18 hledger-1.33
close enhancements, hledger-ui ‘dark’ theme, GHC 9.8 support, Apple ARM binaries
hledger 1.33
Breaking changes
-
expr:boolean queries, introduced in hledger 1.30 (2023), no longer allowdate:to be used within anORexpression, avoiding unclear semantics which confuse our reports. If you’d like to improve this, see #2178. #2177 #2178 -
Some error messages (date parse errors, balance assertion failures) have changed, which might affect error-parsing add-ons like flycheck-hledger.
Fixes
-
add,import,web: On MS Windows, don’t allow writing to files whose name ends with a period, since it can cause data loss; raise an error instead. I made this change in hledger 1.15 (2019), but it never worked; now it does. #1056 -
balance --budget: The budget report in tree mode was omitting parent accounts with no actual or goal amounts and a single child, instead of showing them as a prefix of the child’s name. Now it always shows them, on a line of their own (a bit like--no-elide). It’s not a perfect fix, but the budget report code is twisty. #2071 -
check tags: The specialdateanddate2tags, and themodifiedand_modifiedtags generated by--auto, are now also implicitly declared. #2148, #2119 -
Regular expression match group references in CSV
ifrules, added in hledger 1.32, did not work right when multiple if conditions matched a CSV record. This is now fixed; match group references are now scoped to their localifblock. #2158 (Jonathan Dowland) -
roinow correctly interacts with--value. #2190 (Dmitry Astapov) -
hledger now requires process-1.6.19.0+ to avoid any vulnerabilities on Windows from HSEC-2024-0003.
Features
-
closehas had some enhancements for usability (#2151):-
It now excludes equity accounts by default; and always excludes the balancing account.
-
It has new
--assertand--assignmodes, for generating transactions which make balance assertions or balance assignments. There is also a--assertion-typeoption for changing the assertion/assignment type. -
It adds a tag to generated transactions, named
start,assertorretaindepending on the mode. -
The
starttag’s value will be a guess of the new file’s name, inferred by incrementing a year number in the current file name. Eg,hledger close --migrateon2024.journalwill add the tagstart:2025.journalto both transactions. Tags like this can be helpful when reading multiple files, for excluding closing and opening balances transactions (eg withnot:tag:start=2025). -
You can set different tag values by writing the mode option with an argument. Eg:
hledger close --migrate=NEWFILENAME. -
closenow supports--roundfor controlling display of decimal places, likeprint. -
examples/multi-year/is examples/tutorial for managing multiple files with theclosecommand.
-
Improvements
-
statshas had some improvements:-
It now also shows some information about memory usage, when hledger is built or is running with the GHC Run Time System available. (Try
hledger stats +RTS -T.) -
The default output is now more private, hiding file paths and commodity symbols. Those can be added by the new
-v/--verboseflag. -
Output is now more compact and more likely to fit in 80-character lines.
-
When generating multiple outputs with a report interval, reports are now separated by an empty line.
-
-
Several more kinds of Unicode space are allowed for separating digit groups in numbers. We now support (my guess of the ones that might show up in real world CSV files): space, no-break space, en space, em space, punctuation space, thin space, narrow no-break space, medium mathematical space.
-
Glob patterns in
$LEDGER_FILEare now respected. Eg, setting it to*.journalor2???.journalnow works as expected. -
When hledger is reading a symbolically-linked journal file, relative paths in include directives are now evaluated relative to the directory of the real linked file, not the directory containing the symbolic link.
-
Date parse errors are now simpler and clearer. They no longer try to repeat (a reconstruction of) the problem date, since the actual problem date is already visible in the highlighted file excerpt.
-
Balance assertion error messages are clearer, and show the difference between expected and actual balance again. With –debug=2 they also show costs.
-
tsv:andssv:file name prefixes are now supported in addition tocsv:. They force the file to be read as a .tsv (tab separated values) or .ssv (semicolon-separated values) file. #2164 (Michael Rees) -
In CSV rules files, commented lines are now allowed within “if tables”. (Dmitry Astapov)
-
balance --budget’s CSV and TSV output now shows zeroes instead of nothing when there’s no amount. -
bs,bse,cf,is: Report sections which are empty now show zero as their subtotal. (aragaer) -
printandcloseadd a trailing decimal mark when needed to disambiguate a single digit group mark. They now also do this for balance assertion and balance assignment amounts. #2176 -
hledger can now be built with GHC 9.8.
-
hledger now requires safe >=0.3.20.
Docs
- add version annotations for features added in 1.32 (hamzashezad)
- add Text encoding section, mention UTF-8 BOM support #2189
- journal: note that
payeeandtagdirectives can’t have tags in comments, unlikeaccount. - journal: clarify how auto postings work.
- journal: list built-in special tag names
- journal: description/payee/note: clarify
- journal: amounts/commodities/numbers: cleanups
- journal: move intro before cheatsheet
- journal: transactions: explain transaction balancing #2135
- journal: transactions: mention debits, credits and sign
- journal: commodity directive: clarify & fix scope of effects #2135
- journal: D directive: clarify scope #2191
- journal: split Decimal marks, Digit group marks
- journal: move complex commodity styles, lot notation topics later
- journal: drop redundant/wrong Querying with cost or value section
- journal: cheatsheet: cleanups
- journal: assertions and ordering/commodities/subaccounts: cleanups
- csv: matchers: clarify, mention !/& limitation #2088
- csv: if tables: explain comments and order of application (Dmitry Astapov)
- add: document the effect of D default commodity directive #815
- balance: cleanups
- balance: budget report: moved “Budgets and subaccounts” to the Cookbook.
- bs,bse,cf,is: update sample output
- bse: note requirements for checking the accounting equation
- close: rewrite, give a better technique for excluding opening/closing balance txns #2151
- import: rename “deduplication” to “skipping”, and rewrite
- examples: expand READMEs, clarify status for examples
- examples: invoicing: cleanups, renames
- examples: invoicing: pandoc-make-invoice: don’t write to $LEDGER_FILE; remove the REMOVE THIS LINE line
- examples: csv: daedalus-transactions: update for current daedalus #2171
Scripts/addons
-
hledger-bar, hledger-simplebal: shellcheck fixes, cleanups (Colin Dean)
-
hledger-bar: Fix an error when NO_COLOR is not defined #2159. Also, it’s now more compliant with the no-color.org spec:
Command-line software which adds ANSI color to its output by default should check for a NO_COLOR environment variable that, when present and not an empty string (regardless of its value), prevents the addition of ANSI color.
so one can now temporarily override $NO_COLOR=1 in the environment by setting it empty: NO_COLOR= hledger …
-
hledger-txnsbycat: added
API
- move readFileStrictly to hledger-lib:Hledger.Utils.IO
hledger-ui 1.33
Fixes
- Require process 1.6.19.0+ to avoid any vulnerabilities on Windows from HSEC-2024-0003.
Features
- Add a
darktheme. (Jonathan Dowland)
Improvements
-
Allow building with GHC 9.8.
-
Require safe >=0.3.20.
hledger-web 1.33
Fixes
-
Exclude base64 >=1.0 to avoid compilation failure. #2166
-
Preserve line breaks when showing an error message. #2163 (Martijn van der Ven)
Improvements
-
Zero amounts are now shown with their commodity symbol. This was mainly to make the sidebar more informative, but also affects and hopefully helps amounts displayed elsewhere. #2140
-
Amounts in the sidebar now also have the
amountHTML class. -
Allow building with GHC 9.8.
-
Require safe >=0.3.20.
Docs
- Mention the
-E/--emptyflag for hiding zeros, the non-display of costs, and non-zeros that look like zero because of hidden costs.
project changes 1.33
Misc
- Apple ARM binaries are now included in github releases.
Docs
- REGRESSIONS: we now split the bounty between finder and fixer
- move Developer docs, MOCKUPS, investment-accounting-features to main repo
- merge LINKS into dev docs page; cleanup
- drop unused BACKLOG, TODO pages
credits 1.33
Simon Michael, Jonathan Dowland, Ilja Kocken, Colin Dean, Dmitry Astapov, Vekhir, ShrykeWindgrace, Martijn van der Ven, Michael Rees, aragaer, hamzashezad.
2024-01-28 hledger-1.32.3
hledger 1.32.3
Fixes
-
A performance slowdown since 1.29, especially noticeable with many accounts and transactions, has been fixed. #2153
-
Balance assertions involving mixed-cost balances are checked correctly again (a regression in 1.30). [#2072], [#2137], #2150
-
import –catchup works again (a regression in 1.32). #2156
-
–anon is now a deprecated hidden flag that raises an error, but is still usable as –obfuscate (also hidden). #2133
-
Balance assertion error messages are clearer, and show the diff again.
hledger-ui 1.32.3
-
Use hledger-1.32.3
-
Allow vty 6.2, brick 2.3
hledger-web 1.32.3
- Use hledger-1.32.3
project changes 1.32.3
-
bin/hledger-bar: Fix an error when NO_COLOR is not defined; allow color when NO_COLOR is defined but empty, per no-color spec; and fix shellcheck warnings. #2159 (Colin Dean, Simon Michael)
-
bin/hledger-simplebal: Fix shellcheck warnings. (Colin Dean)
credits 1.32.3
Simon Michael, Colin Dean.
2023-12-31 hledger-1.32.2
hledger 1.32.2
Fixes
-
In CSV field assignments, %FIELD interpolation and
\ncan be used together again. #2134 -
In timedot data, numbers beginning with a decimal point are accepted again. #2130
-
In a
balance --budgetreport,--layout=tallno longer hides commodity symbols. -
Value reports seeing a pathological price chain with 1000 or more steps now write their warning to the console, not a debug log file.
Improvements
- Allow megaparsec 9.6
Docs
- Updated: Queries, Periodic transactions, Auto postings, Assertions and costs, Budget report
hledger-ui 1.32.2
Features
- hledger-ui is now available on Windows (ShrykeWindgrace)
Improvements
-
Use Notepad as default editor on Windows (ShrykeWindgrace)
-
Allow brick 2.2 (Vekhir)
-
Allow megaparsec 9.6
hledger-web 1.32.2
Fixes
-
Startup messages are more accurate and informative, eg with
--socket. #2127 -
The non-working
--file-urloption has been dropped for now. #2139
Improvements
-
Allow megaparsec 9.6
-
hledger-web’s tests now respect and can test command line options.
-
hledger-web’s tests now run the app at 127.0.0.1 and port 5000, rather than “any of our IPv4 or IPv6 addresses” and 3000.
2023-12-07 hledger-1.32.1
hledger 1.32.1
-
Fixed:
importwith multiple files now updates .latest files correctly. (#2125) -
Fixed:
print --round=hardnow properly pads/rounds amounts with inferred costs. (#2123) -
CSV matcher syntax: mention that ! and & can’t be used in the same line yet. (#2088)
-
Drop the “a difference of …” line from balance assertion failure output. I feel it made the message harder to read and isn’t really necessary.
-
Declaring the empty payee name with
payee ""now works, to lethledger check payeesaccept payee-less transactions. (#2119) -
Built-in tags with special meaning like
type:andt:are now implicitly declared, so using type: in account declarations or generating t: with timedot letters won’t causehledger check tagsto fail. (#2119)
hledger-ui 1.32.1
- Use hledger-1.32.1
hledger-web 1.32.1
- Use hledger-1.32.1
2023-12-01 hledger-1.32
More precision control, beancount output, TSV output, –summary-only, strict/idempotent import, CSV rule enhancements, timedot letters, fixes.
hledger 1.32
Breaking changes
-
Display styles and display precision are now managed more carefully during calculations and output, fixing a number of issues (#2111, “Precisiongeddon”). In brief:
-
Cost and value reports, such as
print -V, now (1) consistently apply commodity display styles, and (2) do not add or discard decimal digits unnecessarily. (#2105) -
When “infinite decimals” arise during calculations (eg in value reports, or in
pricesorroioutput), these are now shown limited to 8 decimal digits rather than 255. -
Non-print-like reports no longer add trailing decimal marks to disambiguate digit group marks (this was an unintended regression in 1.31). (#2115)
-
We now document number formatting adjustments made in certain reports and output formats (hledger manual > REPORTING CONCEPTS > Amount formatting, parseability).
-
Features
-
Timedot format supports a new letters syntax for easier tagged time logging. (#2116)
-
printhas a newbeancountoutput format for exporting to Beancount. This prints journal output more likely (though not guaranteed) to be readable by Beancount. -
In CSV rules, matchers using regular expressions can now interpolate their matched texts into the values they assign to fields (field assignment values can reference match groups). (#2009) (Jonathan Dowland)
-
In CSV rules, matchers can be negated by prepending
!. (#2088) (bobobo1618) -
Multi-column balance reports (from
bal,bs,isetc.) can use the new--summary-onlyflag (--summaryalso works) to display just the Total and Average columns (if enabled by--row-totaland-A/--average) and hide the rest. (#1012) (Stephen Morgan) -
All commands that suport csv output now also support
tsv(tab-separated values) output. The data is identical, but the fields are separated by tab characters and there is no quoting or escaping. Tab, carriage return, and newline characters in data are converted to spaces (this should rarely if ever happen in practice). (#869) (Peter Sagerson).
Improvements
-
Journal format no longer fails to parse Ledger-style lot costs with spaces after the
{, improving Ledger compatibility. -
importnow does not update any .latest files until it has run without error (no failing strict checks, no failure while writing the journal file). This makes it more idempotent, so you can run it again after fixing problems. -
printnow shows zeros with a commodity symbol and decimal digits when possible, preserving more information. -
printhas a new option for controlling amount rounding (#2085):-
--round=none- show amounts with original precisions (default; like 1.31; avoids implying less or more precision than was recorded) -
--round=soft- add/remove decimal zeros in non-cost amounts (like 1.30 but also affects balance assertion amounts) -
--round=hard- round non-cost amounts (can hide significant digits) -
--round=all- round all amounts and costs
For the record:
printshows four kinds of amount: posting amounts, balance assertion amounts, and costs for each of those. Past hledger versions styled and rounded these inconsistently. Since 1.31 they are all styled, and since 1.32 they are rounded as follows:hledger-1.32 print amt cost bal balcost (default) none none none none –round=soft soft none soft none –round=hard hard none hard none –round=all hard hard hard hard -
-
The
pricescommand has had a number of fixes and improvements (#2111):-
It now more accurately lists the prices that hledger would use when calculating value reports (similar to what you’d see with
hledger bal -V --debug=2). -
The –infer-reverse-prices flag was confusing, since we always infer and use reverse prices; it has been renamed to
--show-reverse. -
--show-reverseand--infer-market-pricesflags now combine properly. -
--show-reversenow ignores zero prices rather than giving an error. -
Price amounts are now shown styled.
-
Price amounts are now shown with all their decimal digits; or with 8 decimal digits if they appear to be infinite decimals (which can arise with reverse prices).
-
Filtering prices with
cur:oramt:now works properly.
-
Fixes
-
printnow styles balance assertion costs consistently, like other amounts. -
importnow works with-s/--strict. And more generally, when reading multiple input files, eg with multiple-foptions, strict checks are done only for the overall combined journal (not for each individual file). (#2113) -
tag:queries now work when reading CSV files. (#2114) -
Using a
.jsonor.sqlfile extension with-o/--outputfilenow properly selects those output formats. -
Auto postings no longer break redundant equity/cost detection and transaction balancing. (#2110)
-
Amounts set by balance assignment now affect commodity styles again. (#2091, a regression in 1.30)
-
Timedot quantities with units are parsed more accurately. Eg a quantity like “15m” was evaluated as 0.249999999 not 0.25, and since hledger 1.21, it was printed that way also. Now we round such quantities to two places during parsing to get exact quarter-hour amounts. (#2096)
-
The
democommand no longer triggers a JSON decode error in asciinema 2.3.0. It now also shows a better error message if asciinema fails (#2094). -
Failing balance assertions with a cost now show correct markers in the error message. (#2083)
Docs
-
New:
- Amount formatting, parseability
- Started new code docs for developers, based in the Hledger module’s haddock
-
Updated:
- aregister
- commodity directive
- Commodity display style
- if table
- Decimal marks, digit group marks
- Regular expressions
- Timedot
hledger-ui 1.32
Fixes
-
The V key now preserves the valuation mode specified at the command line, if any. (#2084)
-
The hledger-ui package no longer wastefully builds its modules twice.
hledger-web 1.32
Features
- The hledger-web app on the Sandstorm cloud platform has been updated to a recent version (Jacob Weisz, #2102), and now uses Sandstorm’s access control. (Jakub Zárybnický, #821)
Improvements
-
The –capabilities and –capabilities-header options have been replaced with an easier
--allow=view|add|edit|sandstormoption.addis the default access level, whilesandstormis for use on Sandstorm. UI and docs now speak of “permissions” rather than “capabilities”. (#834) -
The Sandstorm app’s permissions and roles have been renamed for clarity. (#834)
-
Permissions are now checked earlier, before the web app is started, producing clearer command line errors when appropriate.
-
Account’s
adeclarationinfofield is now included in JSON output. (#2097) (S. Zeid)
Fixes
-
The app can now serve on address 0.0.0.0 (exposing it on all interfaces), which previously didn’t work. (#2099) (Philipp Klocke)
-
The broken “File format help” link in the edit form has been fixed. (#2103)
project changes 1.32
Scripts/addons
-
hledger-install.sh: replaced hledger-stockquotes with pricehist
-
added gsheet-csv.hs: fetch a google sheet as CSV
-
added hledger-report1: an example custom compound report, with haskell and bash versions
-
justfile: updated import, time report scripts
Examples
-
New:
- Fidelity CSV rules
-
Updated:
- roi-unrealised.ledger (Charlie Ambrose)
Docs
-
New:
- Started a weekly This Week In Hledger news post, inspired by Matrix.
- There’s now a News page, for This Week In Hledger etc.
- hledgermatic, an up-to-date, simple journal-first workflow
- How to record journal entries: added
- Reporting version control stats: added
- Moved regression bounty info from the issue tracker to Developer docs > REGRESSIONS.
-
Updated:
- Checking for errors
- Common workflows
- Ledger
- Simon’s old setup
- Videos
- All docs now use the
cliclass instead ofshellfor command-line examples, avoiding inaccurate highlighting.
Infrastructure
-
hledger.org website:
-
Fixed the webhook that was not updating the site on git push.
-
Fixed a problem with cloudflare authentication that was preventing automatic TLS certificate renewal on hledger.org.
-
Updated and committed hledger.org’s caddy config and short urls (redirects)
-
Enabled https for code.hledger.org and site.hledger.org short urls.
-
Updated the stars.hledger.org redirect (we have reached the top 30 github-starred Haskell projects 🌟 🎉).
-
Set up a self-hosted Sandstorm server, and a public hledger-web instance (sandbox.hledger.org) in it that is fully writable (until spammers find it). Use it as a pastebin for examples, eg.
-
-
Github CI (continuous integration) workflows have been optimised somewhat:
-
Scheduled weekly builds have been disabled, as they were propagating to forks and running wastefully there in some cases.
-
Some repeated rebuilding of the hledger-lib and hledger packages that seems unnecessary has been stopped.
-
hledger-ui no longer builds its modules twice.
-
Haddock testing now done only at release time.
-
renamed main CI workflow and branch to “ci”
-
-
Tools:
-
.ghci: added an :rmain alias, which is like :main but reloads first - saves typing, and is useful eg when changing –debug level.
-
make haddock-watch is now fast
-
Finance
-
Updated project finance scripts, regenerated the journal with consistent precisions.
-
Updated reports with the last few months of data from Open Collective.
credits 1.32
Simon Michael, Jonathan Dowland, S. Zeid, Charlie Ambrose, Jacob Weisz, Peter Sagerson, Philipp Klocke, Stephen Morgan, bobobo1618.
2023-09-03 hledger-1.31
More tolerant equity/cost matching; print amounts in original style; multi-pivot.
hledger 1.31
Features
- Multi-pivot: the –pivot option now accepts multiple arguments, colon-delimited, to construct account names from multiple fields. (#2050, Eric Mertens)
Improvements
-
The
printcommand now more closely replicates the original journal amount styles, which is helpful when round-tripping / cleaning up journal files:-
Amounts in conversion transactions could be displayed rounded to a lower precision; this no longer happens. (#2079)
-
Amounts could be displayed with extra zeros after the decimal mark; this no longer happens.
-
Amounts could display with a different precision if the journal included a timedot file; this no longer happens.
-
Costs in balance assertions were not displayed with standard styles like other amounts; now they are.
-
Zero amounts were always shown as just “0”; now they are shown with their original commodity symbol and style. (And if an inferred amount has multiple zeros in different commodities, a posting is displayed for each of these.)
-
-
printno longer displays numbers with a single digit group mark and no decimal mark, which are ambiguous and hard to re-parse. Now if a number has digit group marks the decimal mark will always be shown also. Eg1,000(where the comma is a thousands separator) is now shown as1,000.. -
The check command’s
balancedwithautoconversionandbalancednoautoconversionchecks have been renamed toautobalancedandbalanced. -
hledger check recentassertionsnow reports failures at the first posting that’s more than 7 days later than the latest balance assertion (rather than at the balance assertion). This is the thing actually triggering the error, and it is more likely to be visible or at least closer when you are working at the end of a journal file.Also, the suggested sample balance assertion now uses the same commodity symbol as in the failing posting (the first, if there are more than one); and, no longer includes a cleared mark.
-
The import command now shows the file path being imported to.
-
With –pivot,
descis now the preferred spelling for pivoting on description. -
The demo command now ignores an invalid journal file, like the other HELP commands.
-
Debug output for equity conversion postings has been improved, making troubleshooting easier.
-
Allow aeson 2.2, megaparsec 9.5.
Fixes
-
In journal files, valid multicommodity transactions where the matching non-equity postings can’t be auto-detected are no longer considered an error (as they were in hledger 1.29 and 1.30). Now, such transactions are accepted, and –infer-cost has no effect on them. This is similar to the behaviour of –cost, –infer-equity, and –infer-market-prices. (#2045)
-
In journal files, equity conversion postings are now detected more tolerantly, using the same precision as the conversion posting’s amount (#2041). Eg, the following transaction is now accepted:
2023-01-01 Assets -84.01 USD @ 2.495 GEL ; ^ 209.60495 GEL, recognised as a match for the 209.60 below Equity:Conversion 84.01 USD Equity:Conversion -209.60 GEL Assets 209.60 GEL -
The roi command now reports TWR per period and overall TWR for multi-period reports. (#2068, Dmitry Astapov)
-
The commands list no longer shows bar when hledger-bar is not installed (#2065), and had a few other cleanups.
hledger-ui 1.31
Improvements
- Allow megaparsec 9.5
hledger-web 1.31
Improvements
- Allow aeson 2.2, megaparsec 9.5
project changes 1.31
Scripts/addons
-
ft, tt shell scripts for collecting financial and time reports
-
A justfile implementation of ft and tt
Examples
-
self-tracking
-
RPG ledger (Eric Mertens)
Docs
Infrastructure
-
tools, CI: checkembeddedfiles, checkversions
-
Shake: avoid making empty commits
-
make functest-PAT: runs a subset of functional tests
-
Provide a ghc-tags.yaml file to make use of ghc-tags with Hledger easy.
ghc-tags is a standalone tool to replace the formerly-built-in “:ctags” feature (and I presume “:etags”) in GHCi. These walked over the source and produced a TAGS file (in vim-compatible ctags or Emacs-compatible etags format) that allows the relevant editors to quickly navigate around function definitions.
ghc-tags trips over some of the CPP used in Hledger. The solution is to provide ghc-tags with explicit CPP defines via a YAML file. However, if a YAML file is provided, one also must specify the source paths, as the tool XORs config file | paths-on-command-line.
See https://github.com/arybczak/ghc-tags/issues/6 for more information. (Jonathan Dowland)
credits 1.31
Simon Michael, Dmitry Astapov, Eric Mertens, Jay Neubrand, Jonathan Dowland.
2023-06-02 hledger-1.30.1
hledger 1.30.1
Fixes
- Add missing files to Hackage release, making it buildable.
Docs
- Replace note about repeated options.
2023-06-01 hledger-1.30
Boolean queries, easier CSV file management, built-in demos, hledger-ui cash accounts screen, fixes.
hledger 1.30
Breaking changes
-
The CSV reader now properly skips all empty lines, as specified by docs. Previously, inner empty lines were not being skipped automatically. You might need to adjust the
skipcount in some CSV rules files. (#2024) -
Timedot format now generates a single multi-posting transaction per date line, and supports comments and tags on all lines. (#1754)
-
Timeclock format now supports comments and tags. Descriptions can no longer contain semicolons. (#1220)
Features
-
CSV rules files can now be read directly, as in
hledger -f foo.csv.rules CMD. By default this will read data from foo.csv in the same directory. -
CSV rules files can use a new
source FILErule to specify the data file, with some convenience features:-
If the data file does not exist, it is treated as empty, not an error.
-
If FILE is a relative path, it is relative to the rules file’s directory. If it is just a file name with no path, it is relative to
~/Downloads/. -
If FILE is a glob pattern, the most recently modified matched file is used.
This helps remove some of the busywork of managing CSV downloads. Most of your financial institutions’s default CSV filenames are different and can be recognised by a glob pattern. So you can put a rule like
source Checking1*.csvin foo-checking.csv.rules, periodically download CSV from Foo’s website accepting your browser’s defaults, and then runhledger import checking.csv.rulesto import any new transactions. The next time, if you have done no cleanup, your browser will probably save it as something like Checking1-2.csv, and hledger will still see that because of the * wild card. You can choose whether to delete CSVs after import, or keep them for a while as temporary backups, or archive them somewhere. (Experimental) -
-
The balance command has a new –count report type which reports posting counts instead of amounts.
-
Full boolean queries, allowing arbitrary use of AND, OR, NOT (case insensitive) and parentheses for grouping, are now supported. For backward compatibility, these require an
expr:prefix. Existing queries work as before, and you can mix and match the old and new styles if you like. (Chris Lemaire) -
demo: This new command plays brief asciinema screencasts explaining various features and use cases. We will add more of these over time. (Experimental)
Improvements
-
Add-on commands can now have
.js,.lua, or.phpfile extensions. -
Generated and modified transactions and postings have the same hidden tags (beginning with underscore) as before, but no longer have visible tags added by default. Use
--verbose-tagsif you want them added. -
We now try harder to ensure
less(and itsmoremode) show our ANSI formatting properly in help output. If you use some other $PAGER, you may have to configure it yourself to show ANSI (or disable ANSI entirely, eg by setting NO_COLOR=1). This is now documented in hledger manual > Paging. (#2015) -
The print command’s
--matchmode has been refined. Previously, similarity completely outweighed recency, so a slightly-more-similar transaction would always be selected no matter how old it was. Now similarity and recency are more balanced, and it should produce the desired transaction more often. There is also new debug output (at debug level 1) for troubleshooting. -
Miscellaneous commands list updates. Help has been added for all published add-on commands (like hledger-lots).
-
The help command’s documentation now mentions an issue caused by a too-old
infoprogram, as on mac. (#1770)
Fixes
-
Unbalanced virtual postings with no amount always infer a zero amount. This is fixing and clarifying the status quo; they always did this, but print always showed them with no amount, even with -x, and the behaviour was undocumented.
-
On windows systems with multiple drive letters, the commands list could fail to show all installed add-ons. (#2040)
-
Balancing a transaction with a balance assignment now properly respects costs. (#2039)
-
The commands list no longer lists non-installed addons. (#2034)
-
Since hledger 1.25, “every Nth day of month” period rules with N > 28 could be calculated wrongly by a couple of days when given certain forecast start dates. Eg
~ every 31st day of monthwith--forecast='2023-03-30..'. This is now fixed. (#2032) -
Postings are now processed in correct date order when inferring balance assignments. (#2025)
-
Posting comment lines no longer disrupt the underline position in error messages. (#1927)
-
Debug output is now formatted to fit the terminal width.
Docs
-
Miscellaneous manual cleanups.
-
Rewrite introductory sections, Date adjustment, Directives, Forecasting, etc.
-
Add Paging section.
-
Remove archaic mentions of
setenv.
API
- Renamed: Hledger.Cli.Commands: findCommand -> findBuiltinCommand
hledger-ui 1.30
Features
- A “Cash accounts” screen has been added, showing
accounts of the
Cashtype.
Improvements
-
The top-level menu screen is now the default screen. Power users can use the
--cash/--bs/--is/--allflags to start up in another screen. -
“All accounts” screen has been moved to the bottom of the list.
-
Screens’ help footers have been improved.
Docs
-
The transaction screen’s inability to update is now noted.
-
Miscellaneous manual cleanups.
hledger-web 1.30
Fixes
- A command line depth limit now works properly. (#1763)
Docs
- Miscellaneous manual cleanups.
project changes 1.30
Scripts/addons
-
hledger-bar: new script for making simple bar charts in the terminal
-
hledger-install: also list cabal, stack, pip tool versions
Examples
-
examples/csv: added a more up-to-date CSV makefile
-
examples/i18: Added sample top level account and type declarations in several languages
Docs
-
A shorter, more example-heavy home page on the website.
-
Simplified website and FAQ structure.
credits 1.30
Simon Michael, Chris Lemaire, Yehoshua Pesach Wallach.
2023-04-07 hledger-1.29.2
hledger 1.29.2
Breaking changes
-
1.29’s cleanup of the
closecommand has been continued. Here are all the changes toclosesince hledger 1.28:-
The default behaviour is now to print only one transaction: a closing transaction.
-
To print both closing and opening transactions as before, use the new
--migrateflag. -
The accounts closed by default are now just the ALE accounts (accounts declared or inferred as type
Asset,Liability, orEquity). If you don’t have account types configured, or to close some other set of accounts, provide query arguments that match them. To close all accounts as before, use a.argument to match them all. -
To print a retain earnings transaction for RX accounts (accounts of type
RevenueorExpense), use the new--retainflag. -
The
equitycommand alias, removed in 1.29, has been restored. -
The
--open-acctoption, removed in 1.29, has been restored. -
The
--closingand--openingflags have been renamed to--closeand--open. (--closehad been removed in 1.29 and is now restored.) -
The docs have been rewritten. Also the 1.29 release notes now mention the breaking change.
-
The command is marked experimental again.
(#2020)
-
Fixes
-
type:queries now “see through” account aliases and pivots, as they did in hledger <1.27, and asacct:queries do. (#2018) -
The corruption in 1.29’s info manual is fixed. (#2023)
-
The 1.29 release notes for periodic reports’/periodic transactions’ start dates have been improved. Also the hledger manual’s “Date adjustment” section has been corrected and clarified.
hledger-ui 1.29.2
Improvements
- A pager is used to show –help output when needed, as in
hledger.
Fixes
- The corruption in 1.29’s info manual is fixed. (#2023)
hledger-web 1.29.2
Improvements
- A pager is used to show –help output when needed, as in
hledger.
Fixes
- The corruption in 1.29’s info manual is fixed. (#2023)
project changes 1.29.2
Scripts/addons
- hledger-install: re-enable hledger-interest, hledger-iadd; add hledger-lots
credits 1.29.2
Simon Michael
2023-03-16 hledger-1.29.1
hledger 1.29.1
Improvements
-
Hledger.Cli.Script now also exports
Control.Applicative Control.Concurrent Data.Char Data.Functor System.IO System.IO.Errorand new string helpers
strip1Char stripBy strip1By -
Allow building with GHC 9.6.1 (#2011)
Fixes
- The stats report no longer displays “Exact” in front of dates. (#2012)
Docs
- remove duplicate in
hledger closedocs (Yehoshua Pesach Wallach)
hledger-ui 1.29.1
- Allow building with GHC 9.6.1 (#2011)
hledger-web 1.29.1
- Allow building with GHC 9.6.1 (#2011)
2023-03-11 hledger-1.29
Tag checking, flexible multi-period start dates, flexible cost/conversion posting combining, new commands list, hledger manual reorg, easier close command, 10% more Ledger file compatible
hledger 1.29
Breaking changes
- Weekly reports are no longer automatically adjusted to start on a monday; in some cases you might need to adjust their start date to preserve simple week headings (see below).
Features
-
In journal format there is now a
tagdirective for declaring tag names, and the check command now has atagscheck to enforce use of declared tag names. -
Periodic transactions and multi-period reports can now start on any date. To enable this while still maintaining pretty good backward compatibility, hledger now keeps explicitly specified start dates as they are, but still automatically adjusts inferred start dates to interval boundaries. This means, eg:
-
A periodic rule like
~ monthly from 2023-01-15now works as you’d expect instead of raising an error. This also improves our ability to read Ledger files. -
Period options like
-p 'monthly from 2023/1/15'or-M -b 2023/1/15now start the report on exactly 1/15 instead of being adjusted to 1/1. -
-p 'weekly in 2023-01', which previously was adjusted to start on a monday, now starts exactly on 2023-01-01. This can cause more verbose report headings. To ensure simple week headings, now weekly reports must start on a monday, eg-p 'weekly from 2022-12-26 to 2023-02'. (#1982)
-
-
You can now freely combine @/@@ notation and conversion postings in a single transaction. This can help readability, and also allows more flexibility when recording cost. hledger will check that the two notations are in agreement, and ignore the redundancy if they are. (Conversion postings are postings to accounts with type
V/Conversionor nameequity:conversion/equity:trade/equity:trading, or subaccounts of these. See also COST.)
Improvements
-
hledger’s commands list has been reorganised for clarity. More add-on commands are now recognised and categorised, and unrecognised add-on commands are listed in a more compact multi-column layout. (Simon Michael, Michael Grünewald)
-
hledger’s commands list and command line help now use ANSI (bold headings) when supported.
-
hledger’s commands list and command line help now use a pager (respecting $PAGER) for long output except on MS Windows.
-
hledger’s
--versionoutput no longer shows+for dev builds made in dirty repos (it was buggy). -
The add command’s Description completions now also include payee names (declared with
payeeor recorded in transactions with|), not just full descriptions. -
aregister now supports HTML output. (#1996) (Jonathan Dowland)
-
aregister now shows a “ (matching query)“ hint in report title when extra query args (other than date: or depth:) are used, to reduce confusion.
-
close now has three modes,
--retain/--migrate/--open, clarifying its uses and providing more useful defaults. -
register-match is now the
--matchmode of the register command. (This command was used by ledger-autosync at one point; if you still need it, hopefullyregister --matchworks similarly.) -
print-unique has been dropped, because it doesn’t support print’s options, it disorders same-day transactions, I don’t know of any users or use cases, and it could easily be recreated as an addon script.
-
print’s JSON output now also includes source positions for
--forecasttransactions. (Chris Lemaire) -
Journal format now allows the empty commodity symbol to be written as
"", so it’s now possible to declare market prices for it:P 2022-01-01 "" $100. This can be useful for timedot data. -
Inferring costs from equity now happens after transaction balancing, not before. As a result,
--infer-costsnow works in transactions where an amount is left blank. -
accountdeclarations now reject parenthesised account names, reducing confusion. (Chris Lemaire) -
Our journal reader now accepts more Ledger syntax, improving Ledger file compatibility (#1962). We now test our ability to at least read the sample journals from Ledger’s baseline functional tests, and our success rate has improved from 80% to 90% since 1.28.
sinceis accepted as synonym offromin period expressionsapply yearandyearare accepted as synonyms ofY(lot notes)in amounts and((valuation expressions))after amounts are now ignored- directives
A,assert,bucket,capture,check,define,expr,eval,python,value,apply fixed,apply tag,end apply fixed,end apply tag,end apply yearare now ignored - subdirectives of
payee,tag, andcommodity(other thanformat) are now ignored popdirective is no longer supported
-
When reading CSV, we now check that assigned account names are valid (parseable). (#1978)
Fixes
-
aregister now handles an extra account query correctly. (#2007)
-
balance’s
--helpnow mentions--layout=tidy -
Balance commands with
--layout=barenow generate proper table layout in HTML output. -
register’s
-w/--widthoption no longer gives ugly parse error messages. -
stats’s
--helpno longer wrongly claims to support -O/–output-format. -
Balance assignments with a cost now generate a correct balance assertion. (#1965)
-
The CSV reader now properly skips header lines before attempting to parse records. (#1967)
Scripts/addons
-
Scripts can now use Hledger.Cli.Script, a convenient new prelude which helps reduce import boilerplate. It currently re-exports:
Control.Monad Data.Either Data.List Data.Maybe Data.Ord Data.Time Text.Printf hiding (formatString) Data.Text (Text, pack, unpack) Safe hiding (at) System.Directory System.Environment System.Exit System.FilePath System.Process Hledger Hledger.Cli Hledger.Cli.Main (argsToCliOpts)(Not much of Data.Text/Data.Text.IO because those need to be qualified.)
Docs
- chunk the hledger manual into parts, rename and rearrange sections for better structure/flow
- add a cheatsheet demonstrating all the main journal features that I recommend
- move a number of my not-so-recommended journal features into a less visible “Other syntax” section
- add: payees/descriptions completion
- areg: more advice on account-matching
- bal: –budget: clarify use of print –forecast
- bal: budget: compare with forecasting; add some tips
- balance cleanups/reorder
- check: adjacentconversionpostings was dropped
- cli: balance: fix link to Budgeting page
- cli: fix all links to Journal > Tags / Commands > tags
- codes: improve example suggested by Rob Nielsen
- csv, timeclock, timedot: clarify comment lines (#1953)
- csv: add new coinbase example
- csv: clarify amount-in/amount-out docs (#1970)
- csv: clarify skip/valid csv semantics (#1967)
- csv: clarify valid CSV requirements and issues (fix #1966)
- csv: cleanup, reorder, CSV rules tips -> Working with CSV
- csv: fix wrong if tables doc; rewrite several sections (#1977)
- csv: flatten, clean up CSV sections
- csv: improve Amount field / Setting amounts
- csv: note -in and -out are used together for one posting (#1970)
- csv: rules factoring tips
- csv: try to clarify how CSV fields and hledger fields work
- document –infer-market-prices with signed costs (#1870)
- fix duplicate market prices heading breaking info navigation
- import: note a pitfall with multifile import
- improve Directives summaries
- introduction/input/output improvements
- journal: cheatsheet: clarify date tag
- journal: rewrite Account names, mention brackets/parentheses (#1915)
- mention pivoting on a tag with multiple values (#1950)
- more cost notation docs; describe Ledger and Beancount cost notation
- more mention of posting order effect on inferring cost (#1959)
- period expressions doc updates
- Removed redundant paragraph in documentation. (J. B. Rainsberger)
- rename directive sections, fix many links
- reorganise commands list, like the CLI
- reorganise bin/README & the Scripts page, add entries for recent scripts
- replace “transaction prices” terminology with “costs”
- tags: discuss multi-values/overriding (#1950)
- update market price inference docs per sol
- Updated section on pivoting. Used synonyms for “member” in cases where there could be confusion with the tag named “member.” (Robert Nielsen)
- use more standard and consistent boilerplate in hledger, ui, web man pages
- virtual postings: improve wording per Robert Nielsen
hledger-ui 1.29
-
In the help dialog, mention that LEFT shows other screens.
-
In the manual, mention shift-up/down config needed for Terminal.app.
hledger-web 1.29
-
The add form’s typeahead now shows non-ascii text correctly. (#1961) (Arsen Arsenović)
-
In the manual, improve –base-url’s description. (#1562)
project changes 1.29
Scripts/addons
- hledger-script-example.hs: rename/cleanup
- sortandmergepostings: new, sorts postings and merges duplicates (Caleb Maclennan, Murukesh Mohanan)
- hledger-register-max: new, prints the posting with largest historical balance
- hledger-git: record shows better error output, no longer force-adds ignored files
- hledger-git: status is fixed, also shows diffs
- hledger-git: add short command aliases r, s, l
- hledger-git: -h is fixed
- hledger-git: pass unrecognised commands to git
- hledger-install: also install hledger-edit, hledger-plot
- hledger-install: add support for installing python packages
- hledger-install: show quieter stack/cabal output
- hledger-install: align install status list
- hledger-install: don’t list hledger-install.sh in PATH
- hledger-install: drop hledger-iadd for now https://github.com/hpdeifel/hledger-iadd/issues/71
Docs
- move most dev docs to doc/
- Scripting hledger: move plugin types table here
- Scripts: add hledger-plot, hledger-edit, hledger-fifo (Yann Büchau, Simon Michael)
- update lots mockups, move to Mockups page
- split Contributor Guide into Contributor Quick Start, LINKS, ISSUES
- add REPOS, FILES, DECISIONS
- CREDITS: updates, link to github contributors list
Infrastructure
- pr template: mention COMMITS page and prefix convention (#1997)
- make ghc 9.4 and current stackage nightly the default for dev builds
- require megaparsec 9.3+ in dev builds, for its useful dbg tool
- make site-watch: fix runaway recursion, be more verbose
- new make rules: man-watch
- new tools: ciwatch, push, pushdocs, gtree
- misc process updates
credits 1.29
Simon Michael, Chris Lemaire, Caleb Maclennan, Jonathan Dowland, J. B. Rainsberger, Michael Grünewald, Robert Nielsen, Yann Büchau.
2022-12-01 hledger-1.28
new hledger-ui screens, better debug output; accounts, print, csv-reading improvements; new hledger-move, watchaccounts scripts
hledger 1.28
Features
-
The
accountscommand has new flags:--undeclared(show accounts used but not declared),--unused(show accounts declared but not used), and--find(find the first account matched by the first command argument, a convenience for scripts). Also-uand-dshort flags have been added for--usedand--declared. -
A new CSV rule
intra-day-reversedhelps generate transactions in correct order with CSVs where records are reversed within each day. -
CSV rules can now correctly convert CSV date-times with a implicit or explicit timezone to dates in your local timezone. Previously, CSV date-times with a different time zone from yours could convert to off-by-one dates, because the CSV’s timezone was ignored. Now,
-
When a CSV has date-times with an implicit timezone different from yours, you can use the
timezonerule to declare it. -
CSV date-times with a known timezone (either declared by
timezoneor parsed with%Z) will be localised to the system timezone (or to the timezone set with theTZenvironment variable).
(#1936)
-
Improvements
-
print –match now respects -o and -O.
-
print –match now returns a non-zero exit code when there is no acceptable match.
-
Support megaparsec 9.3. (Felix Yan)
-
Support GHC 9.4.
Fixes
- In CSV rules, when assigning a parenthesised account name to
accountN, extra whitespace is now ignored, allowing unbalanced postings to be detected correctly.
Scripts/addons
-
bin/hledger-move helps record transfers involving subaccounts and costs, eg when withdrawing some or all of an investment balance containing many lots and costs.
-
bin/hledger-git no longer uses the non-existent git record command. (#1942) (Patrick Fiaux)
-
bin/watchaccounts is a small shell script for watching the account tree as you make changes.
hledger-ui 1.28
Features
-
New “Balance sheet accounts” and “Income statement accounts” screens have been added, along with a new top-level “Menu” screen for navigating between these and the “All accounts” screen.
-
hledger-ui now starts in the “Balance sheet accounts” screen by default (unless no asset/liability/equity accounts can be detected, or command line account query arguments are provided). This provides a more useful default view than the giant “All accounts” list. Or, you can force a particular starting screen with the new –menu/–all/–bs/–is flags (eg,
hledger-ui --allto replicate the old behaviour).
Improvements
-
The ENTER key is equivalent to RIGHT for navigation.
-
hledger-ui debug output is now always logged to ./hledger-ui.log rather than the console, –debug with no argument is equivalent to –debug=1, and debug output is much more informative.
-
Support GHC 9.4.
-
Support megaparsec 9.3 (Felix Yan)
-
Support (and require) brick 1.5, fsnotify 0.4.x.
Fixes
-
Mouse-clicking in empty space below the last list item no longer navigates back. It was too obtrusive, eg when you just want to focus the window. You can still navigate back with the mouse by clicking the left edge of the window.
-
A possible bug with detecting change of date while in –watch mode has been fixed.
API
-
hledger-ui’s internal types have been changed to allow fewer invalid states
and make it easier to develop and debug. (#1889, #1919). -
Debug logging helpers have been added and cleaned up in Hledger.Ui.UIUtils: dbgui dbguiIO dbguiEv dbguiScreensEv mapScreens screenId screenRegisterDescriptions
hledger-web 1.28
Improvements
-
–debug with no argument is now equivalent to –debug=1.
-
Allow megaparsec 9.3 (Felix Yan)
-
Support GHC 9.4
project changes 1.28
Docs
- Miscellaneous improvements.
Examples
- Indian National Pension Service CSV rules (Pranesh Prakash)
Infrastructure
-
make site-watch: switch from entr to watchexec.
-
make hoogle-setup, hoogle-serve: run a local hoogle on hledger code.
-
make man-watch-PROG: watch a hledger program’s man page as source files change.
credits 1.28
Simon Michael, Felix Yan, Patrick Fiaux.
2022-09-18 hledger-1.27.1
hledger 1.27.1
Fixes
- Balance commands using
-T -O htmlno longer fail with an error when there is no data to report. (#1933)
hledger-ui 1.27.1
- Uses hledger-1.27.1
hledger-web 1.27.1
Fixes
-
The add form no longer gives an error when there is just a single file and no file field showing. (#1932)
-
Uses hledger-1.27.1
2022-09-01 hledger-1.27
Infer costs from equity postings, new error checks, improved error messages, fixes.
hledger 1.27
Features
-
hledger check recentassertions(and flycheck-hledger in Emacs if you enable this check) requires that all balance-asserted accounts have a balance assertion within 7 days before their latest posting.This helps remind you to not only record transactions, but also to regularly check account balances against the real world, to catch errors sooner and avoid a time-consuming hunt.
-
The –infer-costs general flag has been added, as the inverse operation to –infer-equity. –infer-costs detects commodity conversion transactions which have been written with equity conversion postings (the traditional accounting notation) and adds PTA cost notation (@@) to them (allowing cost reporting). See https://hledger.org/hledger.html#equity-conversion-postings . (Stephen Morgan)
Improvements
-
Many error messages have been improved. Most error messages now use a consistent, more informative format. (#1436)
-
The accounts command has a new –directives flag which makes it show valid account directives which you can paste into a journal.
-
The accounts command has a new –positions flag which shows where accounts were declared, useful for troubleshooting. (#1909)
-
Bump lower bounds for Diff and githash. (Andrew Lelechenko)
-
GHC 8.6 and 8.8 are no longer supported. Building hledger now requires GHC 8.10 or greater.
Fixes
-
Account display order is now calculated correctly even when accounts are declared in multiple files. (#1909)
-
At –debug 5 and up, account declarations info is logged. (#1909)
-
hledger aregister and hledger-ui now show transactions correctly when there is a type: query. (#1905)
-
bal: Allow cumulative gain and valuechange reports. Previously, –cumulative with –gain or –valuechange would produce an empty report. This fixes this issue to produce a reasonable report. (Stephen Morgan)
-
bal: budget goal amounts now respect -c styles (fixes #1907)
-
bal: budget goals now respect -H (#1879)
-
bal: budget goals were ignoring rule-specified start date
-
cf/bs/is: Fixed non-display of child accounts when there is an intervening account of another type. (#1921) (Stephen Morgan)
-
roi: make sure empty cashflows are skipped when determining first cashflow (Charlotte Van Petegem) Empty cashflows are added when the begin date of the report is before the first transaction.
Scripts/addons
-
https://hledger.org/scripts.html - an overview of scripts and addons in bin/.
-
paypaljson, paypaljson2csv - download txns from paypal API
-
hledger-check-postable.hs - check that no postings are made to accounts with a postable:(n|no) tag
-
hledger-addon-example.hs - script template
hledger-ui 1.27
Improvements
-
At –debug=2 and up, log debug output to ./debug.log.
-
Use/require brick 1.0+. (#1889)
-
Use hledger 1.27
hledger-web 1.27
Improvements
-
Improve the add form’s layout and space usage.
-
Pre-fill the add form’s date field.
-
Highlight today in the add form’s date picker.
-
Focus the add form’s description field by default.
-
Allow an empty description in the add form.
-
Use hledger 1.27
Fixes
- Respect the add form’s file selector again. (Simon Michael, Kerstin, #1229)
project changes 1.27
Docs
-
https://hledger.org/ERRORS.html - an overview of hledger’s error messages.
-
Rewrite/consolidate cost and conversion docs.
-
New template for github releases, with improved install instructions for binaries.
-
Add modern windows binary install instructions. (Lazar Lazarov, Simon Michael)
-
Fix tables of contents in developer documentation. (Alex Hirzel)
-
Update ACHIEVEMENTS. (Alex Hirzel)
-
Corrected the extension for the CREDITS file. (Pranesh Prakash)
-
Fix broken link in bin/README.md. (David D Lowe)
Examples
- Add example for capital one credit cards CSV. (max thomas)
Process
-
Revive github projects, set up http://projects.hledger.org shortcut url
-
Many cleanups and improvements to the CI test and binary-generating github actions. The CI tests for master now also include hledger-lib’s doctests.
-
All packages now disallow name shadowing in their code.
-
make scc gives a modern report of code line counts.
-
make ghci-unit-test loads hledger-lib unit tests in GHCI.
credits 1.27
Simon Michael, Stephen Morgan, Alex Hirzel, Pranesh Prakash, David D Lowe, Charlotte Van Petegem, Max Thomas, Andrew Lelechenko.
2022-07-11 hledger-1.26.1
hledger 1.26.1
- require safe 0.3.19+ to avoid deprecation warning
hledger-ui 1.26.1
-
support doclayout 0.4, brick 0.72+
-
require safe 0.3.19+ to avoid deprecation warning
2022-06-04 hledger-1.26
Miscellaneous improvements.
hledger 1.26
Improvements
-
registerandaregisterhave been made faster, by-
considering only the first 1000 items for choosing column widths. You can restore the old behaviour (guaranteed alignment across all items) with the new
--align-allflag. (#1839](https://github.com/simonmichael/hledger/issues/1839), Stephen Morgan) -
discarding cost data more aggressively, giving big speedups for large journals with many costs. (#1828, Stephen Morgan)
-
-
Most error messages from the journal reader and the
checkcommand now use a consistent layout, with an “Error:” prefix, line and column numbers, and an excerpt highlighting the problem. Work in progress. (#1436) (Simon Michael, Stephen Morgan) -
hledger check ordereddatesnow always checks all transactions (previously it could be restricted by query arguments). -
The
--pivotoption now supports astatusargument, to pivot on transaction status. -
Update bash completions (Jakob Schöttl)
Fixes
-
Value reports with
--date2and a report interval (likehledger bal -VM --date2) were failing with a “expected all spans to have an end date” error since 1.22; this is now fixed. (#1851, Stephen Morgan) -
In CSV rules, interpolation of a non-existent field like
%999or%nosuchfieldis now ignored (previously it inserted that literal text). Note this means such an error will not be reported; Simon chose this as the more convenient behaviour when converting CSV. Experimental. (#1803, #1814) (Stephen Morgan) -
--infer-market-pricewas inferring a negative price when selling. (#1813, Stephen Morgan) -
Allow an escaped forward slash in regular expression account aliases. (#982, Stephen Morgan)
-
The
tagscommand now also lists tags from unused account declarations. It also has improved command-line help layout. (#1857) -
hledger accountsnow shows its debug output at a more appropriate level (4).
hledger-ui 1.26
- Uses hledger 1.26.
hledger-web 1.26
Fixes
- Don’t add link URLs when printing.
Improvements
-
Now builds with GHC 9.2.
-
Uses hledger 1.26.
project changes 1.26
Scripts/addons
-
renamed hledger-number.sh to hledger-simplebal
-
added hledger-git, hledger-pijul
-
fin (and bin) scripts show available scripts and their help
-
renamed aliases.sh to bashrc
-
Get hledger-print-location working. (Stephen Morgan)
Docs
-
README cleanup, inspired by feedback from README reviewer Lars Wirzenius.
-
Clearer sponsoring info and more complete sponsor lists on website and README.
-
The new https://github.com/simonmichael/hledger_finance repo keeps track of our public finances (on Open Collective, Liberapay etc.)
Examples
- invoice: calculate dates accurately on last days of month
Process
-
Stackage nightly and GHC 9.2 are now the default for dev builds.
-
CI workflows:
- Workflows and binaries have more consistent naming, mentioning platform and architecture.
- The main test workflow is now
linux-x64-test, replacingpushandpull. It runs for both pushes and pull requests, and generates binaries on every run. - Pushes/merges to master, including Simon’s, are required to have passed
linux-x64-teston another github branch first. - Mac and Windows binaries are now stripped also (if applicable).
-
make buildtimes,make buildtimes-cabalshow GHC codegen times.
credits 1.26
Simon Michael, Stephen Morgan, Jakob Schöttl, Patrik Keller.
2022-03-04 hledger 1.25
Account type and tag querying, infer equity postings from @ notation, easily-consumed “tidy” CSV output
hledger 1.25
Breaking changes
-
Journal format’s
account NAME TYPECODEsyntax, deprecated in 1.13, has been dropped. Please useaccount NAME ; type:TYPECODEinstead. (Stephen Morgan) -
The rule for auto-detecting “cash” (liquid asset) accounts in the
cashflowreport has changed: it’s now “all accounts under a top-levelassetaccount, withcash,bank,checkingorsavingin their name” (case insensitive, variations allowed). So if you see a change in yourcashflowreports, you might need to addaccountdirectives withtype:Ctags, declaring your top-most cash accounts.
Features
-
The new
type:TYPECODESquery matches accounts by their accounting type. Account types are declared with atype:tag in account directives, or inferred from common english account names, or inherited from parent accounts, as described at [Declaring accounts > Account types]. This generalises the account type detection ofbalancesheet,incomestatementetc., so you can now select accounts by type without needing fragile account name regexps. Also, theaccountscommand has a new--typesflag to show account types. Eg:hledger bal type:AL # balance report showing assets and liabilities hledger reg type:x # register of all expenses hledger acc --types # list accounts and their types -
The
tag:query can now also match account tags, as defined in account directives. Subaccounts inherit tags from their parents. Accounts, postings and transactions can be filtered by account tag. (#1817) -
The new
--infer-equityflag replaces the@/@@price notation in commodity conversion transactions with more correct equity postings (when not using-B/--cost). This makes these transactions fully balanced, and preserves the accounting equation. For example:2000-01-01 a 1 AAA @@ 2 BBB b -2 BBB $ hledger print --infer-equity 2000-01-01 a 1 AAA equity:conversion:AAA-BBB:AAA -1 AAA equity:conversion:AAA-BBB:BBB 2 BBB b -2 BBBequity:conversionis the account used by default. To use a different account, declare it with an account directive and the newV(Conversion) account type. Eg:account Equity:Trading ; type:V(#1554) (Stephen Morgan, Simon Michael)
-
Balance commands (
bal,bsetc.) can now generate easy-to-process “tidy” CSV data with-O csv --layout tidy. In tidy data, every variable is a column and each row represents a single data point (cf https://vita.had.co.nz/papers/tidy-data.html). (#1768, #1773, #1775) (Stephen Morgan)
Improvements
-
Strict mode (
-s/--strict) now also checks periodic transactions (--forecast) and auto postings (--auto). (#1810) (Stephen Morgan) -
hledger check commoditiesnow always accepts zero amounts which have no commodity symbol. (#1767) (Stephen Morgan) -
Relative smart dates may now specify an arbitrary number of some period into the future or past). Some examples:
in 5 daysin -6 months5 weeks ahead2 quarters ago
(Stephen Morgan)
-
CSV output now always disables digit group marks (eg, thousands separators), making it more machine readable by default. (#1771) (Stephen Morgan)
-
Unicode may now be used in field names/references in CSV rules files. (#1809) (Stephen Morgan)
-
Error messages improved:
- Balance assignments
- aregister
- Command line parsing (less “user error”)
Fixes
-
--layout=bareno longer shows a commodity symbol for zero amounts. (#1789) (Stephen Morgan) -
balance --budgetno longer elides boring parents of unbudgeted accounts if they have a budget. (#1800) (Stephen Morgan) -
roinow reports TWR correctly- when there are several PnL changes occurring on a single day
- and also when investment is fully sold/withdrawn/discounted at the end of a particular reporting period.
(#1791) (Dmitry Astapov)
Documentation
-
There is a new CONVERSION & COST section, replacing COSTING. (#1554)
-
Some problematic interactions of account aliases with other features have been noted. (#1788)
-
Updated: Declaring accounts > Account types
hledger-ui 1.25
- Uses hledger 1.25.
hledger-web 1.25
- Uses hledger 1.25.
project changes 1.25
Scripts/addons
-
hledger-install.sh now also installs Pavan Rikhi’s hledger-stockquotes tool.
-
The bin/hledger-number addon was added.
-
The bin/hledger-check-fancyassertions addon now shows docs in –help.
-
A new invoice-making script was added: examples/invoicing/invoice-script/invoice
Process/tools
-
The RELEASING doc and release process has been updated, and a new helper script added: tools/releaseprep.
make hackageuploadnow only works from a branch named VERSION-branch or VERSION-release. Ie, making releases from master is no longer allowed, a release branch is always required, -
CI: The commitlint check is more robust, and now runs only in the push to master and pull request workflows, and not eg when building release binaries. linux-x64 binaries are now built with ghc 9.0, not 8.10. Workflow, branch, and binary names have been improved.
-
make ghci-ui/make ghcid-uinow use older ghc 8.10 to avoid ghc 9.0-triggered failures. -
hls support: The hie.yaml added to help hls work on mac m1 has been moved out of the way, since it probably makes things worse on other architectures.
credits 1.25
Simon Michael, Stephen Morgan, Dmitry Astapov, Patrik Keller.
2021-12-10 hledger-1.24.1
hledger 1.24.1
Fixes
-
balance --declaredis now filtered correctly by anot:ACCTquery. (#1783) -
More reliable –version output, with commit date and without patch level.
hledger-ui 1.24.1
Fixes
-
An extra “root” account is no longer shown (a regression in 1.24). (#1782)
-
Declared accounts are now filtered correctly by a not:ACCT query. (#1783)
-
More reliable –version output, with commit date and without patch level.
hledger-web 1.24.1
Fixes
- More reliable –version output, with commit date and without patch level.
2021-12-01 hledger-1.24
New report layout options with less eliding, hledger-ui mouse support, misc fixes and improvements.
hledger 1.24
Features
-
balance commands provide more control over how multicommodity amounts are displayed. (And they no longer elide too-wide amounts by default.) The –commodity-column flag has been deprecated and replaced by a new –layout option, with three values:
- wide (the default, shows amounts on one line unelided, like older hledger versions)
- tall (a new display mode, shows one amount per line)
- bare (like the old –commodity-columm, shows one commodity per line with symbols in their own column)
(Stephen Morgan)
-
The balance commands have a new
--declaredflag, causing them to include leaf (ie, non-parent) accounts declared by account directives, even if they contain no transactions yet. Together with-E, this shows a balance for both used and declared accounts. The idea is to be able to see a useful “complete” balance report, even when you don’t have transactions in all of your declared accounts yet. (#1765) -
journal files now support a
decimal-markdirective as a more principled way (thancommoditydirectives) to specify the decimal character in use in that file, to ensure accurate number parsing. (#1670, Lawrence Wu)
Improvements
-
The stats command now shows rough but useful performance stats: run time and processing speed in transactions per second.
-
balance: support the –related flag, like register, showing the other postings from the transactions. (#1469, Stephen Morgan)
-
roi now uses posting dates when available, and honors the –date2 flag. This will not change the results computed for the typical use-case, it just makes “roi” more thorough/consistent. (Dmitry Astapov)
-
aregister now shows transactions’ secondary date if the –date2 flag is used. (#1731)
-
timedot: a D default commodity (and style) declared in a parent journal file will now be applied to timedot amounts. This means they can be priced and valued/converted.
-
cli: The –pretty and –forecast options can now be written after the command name, like other general options. (Stephen Morgan)
-
register -V -H with no interval now values at report end date, like balance. (#1718, Stephen Morgan)
-
Allow megaparsec 9.2.
-
Drop the base-compat-batteries dependency. (Stephen Morgan)
Fixes
-
prices: Do not include zero amounts when calculating amounts for balance assignments. This is not usually a problem, but can get in the way of auto-inferring prices. (#1736, Stephen Morgan)
-
csv: Successfully parse an empty csv file. (#1183, Stephen Morgan)
-
balance: Balance reports with –depth=0 properly report aggregated values, not zero everywhere. (#1761, Stephen Morgan)
-
prices: Do not try to generate prices when there would be a zero denominator. Also correctly generate reverse prices for zero amounts. (Stephen Morgan)
-
csv: Allow both amount-in and amount-out fields to contain a zero. (#1733, Stephen Morgan)
-
balance: Balance reports should consider date: queries when calculating report span with –date2. (#1745, Stephen Morgan)
-
print: auto: The print command should always display inferred amounts for –auto generated postings. (#1276, Stephen Morgan)
hledger-ui 1.24
Features
-
hledger-ui can now be controlled with mouse or touchpad. Click to enter things, click left margin or bottom blank area to return to previous screen, and use mouse wheel / swipe to scroll.
-
In addition to accounts with postings, hledger-ui now also shows declared accounts, even if they are empty (just leaf accounts, not parents). The idea is to show a useful list of accounts out of the box, when all you have is a starter file with account declarations.
Improvements
-
The
Zkey for toggling display of zeroes is now the easier lower-casez. -
The
--watchfeature now has a convenient short flag,-w. -
Drop the base-compat-batteries dependency. (Stephen Morgan)
-
Allow megaparsec 9.2
Fixes
-
When an invalid regular expression is entered at the
/(filter) prompt, we now display an error instead of silently ignoring it. (#1394, Stephen Morgan) -
Entering the register screen now always positions the selection mid-screen. Previously it would be at bottom of screen on the first entry.
-
Report layout in the terminal is now robust with more kinds of wide characters, such as emoji. (#895, Stephen Morgan)
hledger-web 1.24
Improvements
- Allow megaparsec 9.2
project changes 1.24
Software
-
bin/hledger-check-fancyassertions.hs: fix ugly assertion parse errors. (ShrykeWindgrace)
-
bin/hledger-check-tagfiles.hs: Update description, clarify wording. (Pranesh Prakash)
Docs
-
Account types: prioritise the short one-letter names, hide the deprecated legacy syntax.
-
Directives: a more compact and accurate overview.
-
examples/templates/basic: A new starter file set, and a place to collect them.
-
Expose more developer docs as separate web pages: CHANGELOGS, COMMITS, RELEASING, etc.
-
Fix a link to developer workflows. (Joaquin “Florius” Azcarate)
Process
-
PR template: Fix our github PR template to use proper comment syntax, and link to more relevant docs. (toonn)
-
cabal.project: Drop obsolete compatibility comment. (#1365, toonn)
-
Bump default stackage snapshot to one avoiding buggy happy version.
-
bin/changelog: a new helper making changelog edits more pleasant.
-
make throughput{,-dev,-EXE}: reports transactions per second for a range of file sizes with the hledger in PATH, hledger dev build, or named hledger executable.
-
make install-as-FOO: build executables and save as bin/hledger*-FOO
-
perf: bench-ledger.sh for comparative benchmarking with Ledger.
-
CI: commitlint: be more forgiving when we can’t figure out recent commits (don’t check any).
-
CI: commitlint: recognise any commit starting with ‘Merge’ as a merge commit (and ignore it). (Stephen Morgan)
credits 1.24
Simon Michael, Stephen Morgan, toonn, Pranesh Prakash, Dmitry Astapov, ShrykeWindgrace, Joaquin Azcarate, Lawrence Wu.
2021-09-21 hledger-1.23
Capital gains report, separate symbol/number display, command line commodity styling, budget selection, weekday/weekend recurrence, 10% speedup, fixes.
project changes 1.23
Software:
-
The bin/hledger-check-fancyassertions.hs addon script, allowing more complex balance assertions, works again. (#1464, Stephen Morgan)
-
Many code cleanups suggested by hlint (Stephen Morgan)
Docs:
-
Added a public BACKLOG.org to the hledger repo and website.
-
Website updates:
- Reorganised site content.
- Improved page tables of contents.
- Content fixes.
- New docs: Currency conversion. hledger and Beancount/GnuCash/Ledger/Quicken.
-
New examples: systemd and nginx configs for hledger-web (Alan Young)
Tools/process:
-
make site-watchworks again -
make list-commitsandmake showauthorsshow those things. -
Shake cabalfilesnow uses (and requires) hpack in $PATH, to avoid building. It should be the version that’s in the current stack release, to avoid commit conflicts. -
shake: changelogs: A leading semicolon now means “skip most CI steps”, not “omit from changelog”.
-
ci: most steps are skipped if commit message begins with ;.
-
hledger developers now use GHC 9.0/stackage nightly by default. (#1503)
-
Our doctests are disabled with GHC 9 for now to work around an upstream bug. (#1503, #1615)
-
tools/commitlint is a new tool for hledger developers which checks and describes new commit conventions which simplify maintenance of change docs and releasing. It can be run locally while developing, manually or as a pre-commit hook (
ln -sf ../../bin/commitling .git/hooks/commit-msg), and is also run by our CI workflows to check pull requests. https://hledger.org/CONTRIBUTING.html#commit-messages, tools/commitlint (#1602)
hledger 1.23
Features
-
The balance command has a new
--gainreport type, showing unrealised capital gains/losses. Essentially, this is the difference between the amounts’ costs and their total present value. More precisely, between the value of the amounts’ costs and the value of the amounts on the valuation date(s). (Ie, you can report gain in a different currency.) (#1623, #1432, Stephen Morgan, Charlotte Van Petegem) -
The new
-c/--commodity-styleoption makes it easy to override commodity display styles at runtime, eg to adjust the number of decimal places or change the position of the symbol. (#1593, Arjen Langebaerd) -
The balance commands have a new
--commodity-columnflag that displays commodity symbols in a dedicated column, showing one line per commodity and all amounts as bare numbers. (#1559, #1626, #1654, Lawrence Wu, Simon Michael, Stephen Morgan) -
The
balance --budgetoption can now take an argument, a case insensitive description substring which selects a subset of the journal’s periodic transactions for setting budget goals. This makes it possible to keep multiple named budgets in one journal, and select the one you want with –budget’s argument. (#1612) -
Period expressions now support
every weekday,every weekenddayandevery mon,wed,...(multiple days of the week). This is intended for periodic transaction rules used with--forecast(orbal --budget). (#1632, Lawrence Wu) -
The new
--today=DATEoption allows overriding today’s date. This can be useful in tests and examples using relative dates, to make them reproducible. (#1674, Stephen Morgan) -
In CSV rules, multi-line comments are now supported. Newlines in CSV data are preserved, or newlines can be added by writing
\nwhen assigning tocomment,comment1etc. (Malte Brandy)
Improvements
-
Incremental performance improvements; hledger 1.23 is the fastest hledger yet, about 10% faster than 1.22. (Stephen Morgan)
-
registerno longer slows down when there are many report intervals. (#1683, Stephen Morgan) -
Numbers in SQL output now always use decimal period (
.), independent of commodity display styles. (Stephen Morgan) -
--sortnow gives a more intuitive sort oder when there are multiple commodities. Negative numbers in one commodity are always less than positive numbers in another commodity. (#1563, Stephen Morgan) -
--infer-market-pricehas been renamed to--infer-market-prices. (The old spelling still works, since we accept flag prefixes.) -
Our pretty-printed JSON now orders object attributes alphabetically, across all GHC and haskell lib versions.
-
register with a report interval starting on custom dates (eg: `hledger reg -p “every 15th day of month”) now makes the date column wide enough to show the start and end dates. It also wastes less whitespace after the column. (#1655, Stephen Morgan)
-
In JSON output, object attributes are now ordered alphabetically, consistently for all GHC and haskell lib versions. (#1618, Stephen Morgan)
-
JSON output now indents with 2 spaces rather than 4. (Stephen Morgan)
-
The balance commands’
-S/--sort-amountflag now behaves more predictably and intuitively with multiple commodities. Multi-commodity amounts are sorted by comparing their amounts in each commodity, with alphabetically-first commodity symbols being most significant, and assuming zero when a commodity is missing. (#1563, #1564, Stephen Morgan) -
The close command now uses the later of today or journal’s last day as default closing date, providing more intuitive behaviour when closing a journal with future transactions. Docs have been improved. (#1604)
-
Rules for selecting the forecast period (within with –forecast generates transactions) have been tweaked slightly, and some disagreement between docs and implementation has been fixed. Now, the forecast period begins on:
- the start date supplied to the
--forecastargument, if any - otherwise, the later of
- the report start date if specified with -b/-p/date:
- the day after the latest normal (non-periodic) transaction in the journal, if any
- otherwise today.
It ends on:
- the end date supplied to the
--forecastargument, if any - otherwise the report end date if specified with -e/-p/date:
- otherwise 180 days (6 months) from today.
This is more intuitive in some cases. (Eg:
hledger reg --forecast -b 2020-01-01on a journal containing only periodic transaction rules now shows forecast transactions starting from 2020-01-01, rather than from today.) (#1648, #1665, #1667, Stephen Morgan, Simon Michael) - the start date supplied to the
-
Require base >=4.11, prevent red squares on Hackage’s build matrix. (We officially support GHC 8.6+, which means base 4.12, but Hackage shows all packages building successfully with base 4.11/GHC 8.4+ somehow, so it’s still allowed..)
Fixes
-
A rare bug causing incorrect balances to be reported by the cf/bs/bse/is commands, since hledger 1.19, has been fixed. (cf/bs/bse/is with –tree –no-elide –begin DATE and certain account directives could show wrong balances). (#1698, Stephen Morgan)
-
aregister now aligns multicommodity amounts properly (broken since 1.21). (#1656, Stephen Morgan)
-
balance -E(and hledger-ui Z) now correctly show zero parent accounts, fixing a bug introduced in hledger 1.19. (#1688, Stephen Morgan) -
The
roicommand no longer gives an ugly error in a certain case with PnL applied on the first day of investment. (Dmitry Astapov) -
--forecastnow generates transactions up to the day before the specified report end date (instead of two days before). (#1633, Stephen Morgan) -
Certain errors in CSV conversion, such as a failing balance assertion, were always being reported as line 2.
hledger-ui 1.23
Improvements
-
Depend on hledger 1.23.
-
Require base >=4.11, prevent red squares on Hackage’s build matrix.
hledger-web 1.23
Improvements
-
Drop the obsolete hidden
--binary-filenameflag. -
Depend on hledger 1.23.
-
Require base >=4.11, preventing red squares on Hackage’s build matrix.
Fixes
-
Toggle showing zero items properly even when called with –empty. (#1237, Stephen Morgan)
-
Do not hide empty accounts if they have non-empty subaccounts. (#1237, Stephen Morgan)
-
Allow unbalanced postings (parenthesised account name) in the add transaction form. (#1058, Stephen Morgan)
-
An XSS (cross-site scripting) vulnerability has been fixed. Previously (since hledger-web 0.24), javascript code could be added to any autocompleteable field and could be executed automatically by subsequent visitors viewing the journal. Thanks to Gaspard Baye and Hamidullah Muslih for reporting this vulnerability. (#1525, Arsen Arsenović)
credits 1.23
Simon Michael, Stephen Morgan, Lawrence Wu, Jakob Schöttl, Dmitry Astapov, Malte Brandy, Arsen Arsenović, Arjen Langebaerd, Alan Young, Daniel Gröber.
2021-08-07 hledger-1.22.2
hledger 1.22.2
Breaking changes
- aregister no longer hides future transactions by default.
This is a consequence of the fix for
#1638.
It makes aregister consistent, so we think it’s a reasonable change.
So if you have future-dated transactions in your journal which you
don’t want reported, you now must exclude them with
-e tomorrowordate:-tomorrowin the command, as with other reports. (Stephen Morgan)
Improvements
- Timedot format’s doc has been rewritten.
Fixes
-
Make balance assignments in forecasted transactions work again (broken in 1.22.1). Forecast transactions are now generated early and processed in the same way as other transactions. (#1638, Stephen Morgan)
-
aregister preserves the order of same-day transactions again (broken in 1.22.1). (#1642, Stephen Morgan)
hledger-ui 1.22.2
- Use hledger 1.22.2.
hledger-web 1.22.2
- Use hledger 1.22.2.
credits 1.22.2
Simon Michael, Stephen Morgan.
2021-08-02 hledger-1.22.1
hledger 1.22.1
Improvements
-
Bash shell completions (for hledger, hledger-ui, hledger-web) are now included in the hledger package’s release tarballs, making them more likely to be installed by system packages. (Jakob Schöttl)
-
roi docs now discuss how to quote multi-word queries. (#1609, Dmitry Astapov)
-
Allow megaparsec 9.1
Fixes
-
cur:andamt:queries now match the original amounts before valuation and cost conversion, as they did before hledger 1.22. We believe this is the more useful behaviour in practice. (#1625, Stephen Morgan) -
Queries now work better with
register --related, no longer showing duplicate postings when more than one posting in a transaction is matched. (#1629, Stephen Morgan) -
Valuation now works with
register --related. (#1630, Stephen Morgan) -
Auto posting rules now also see inferred amounts, not just explicit amounts. (#1412, Stephen Morgan)
-
The aregister command now properly ignores a
depth:argument. It might now also behave more correctly with valuation or--txn-dates. (#1634, Stephen Morgan) -
Our info manuals now have more robust directory metadata (no subdirectory path), making them more likely to be linked in your top-level Info directory by system packages. (#1594) (Simon Michael, Damien Cassou)
-
The error message for a non-existent input file no longer shows excess double quotes. (#1601, Stephen Morgan)
-
Journal format docs: The commodity directive’s scope is now correctly described (lasts until end of current file).
hledger-ui 1.22.1
Improvements
Fixes
-
Up/down keys work on the transaction screen again (broken since 1.22). (#1607, Stephen Morgan)
-
Fix a possible off-by-one bug with valuation date when using
Vkey on the transaction screen. (If it ever needs to use the journal’s last day as valuation date, use that day, not the day after.)
hledger-web 1.22.1
Improvements
- deps: Allow megaparsec 9.1.
Fixes
- The register chart works again when there are multiple commodities and transaction prices (broken since 1.22). (#1597, Stephen Morgan)
credits 1.22.1
Simon Michael, Stephen Morgan, Jakob Schöttl, Dmitry Astapov.
2021-07-03 hledger-1.22
Optimisations, bugfixes. (announcement)
project changes 1.22
Software:
-
We now provide static executables for GNU/Linux on x64 (amd64) and arm32v7 architectures. These are more portable and more likely to work on your linux system than the dynamic Ubuntu executables we have been providing. These will also be useful for Nextcloud.com users. (#1571) (Garret McGraw)
-
GHC 9.0 support has been added. We have dropped official support for GHC 8.0/8.2/8.4; building hledger now requires GHC 8.6 or newer.
Docs:
- The info manuals now have the proper metadata so you or your
packager can install them with
install-infoand they will appear in info’s Directory. We also provide adirfile making it easy for developers to see the latest dev manuals in their info Directory. (#1585) (Damien Cassou, Simon Michael)
Chat:
-
The hledger IRC channels ( #hledger:libera.chat, #hledger-bots:libera.chat ) moved to Libera.chat.
-
The hledger Matrix room ( #hledger:matrix.org ), is now on at least equal “official” footing with the IRC channel.
-
I upgraded the matrix room to a newer version of the Matrix protocol. This effectively splits it into an old (read only) room and a new room. If you are joined to the old room, you might not have noticed; in your matrix client, please follow the link to the new room, ie #hledger:matrix.org.
-
I briefly bridged the IRC and matrix rooms, because having two chats (four if we consider #plaintextaccounting) is a pain. I hope to try the experiment again at some point.
hledger 1.22
Features
- check: A new
balancednoautoconversioncheck requires transactions to balance without the use of inferred transaction prices. (Explicit transaction prices are allowed.) This check is included in--strictmode. The oldautobalancedcheck has been renamed tobalancedwithautoconversion. (Stephen Morgan)
Improvements
-
Many internal optimisations have been applied (cf hledger-lib changelog). Overall, you can expect most reports to be about 20% faster. The register report is more than 2x faster and uses 4x less memory. (Stephen Morgan)
~/src/hledger$ quickbench -w hledger-1.21,hledger Running 5 tests 1 times with 2 executables at 2021-06-29 13:13:26 HST: Best times: +----------------------------------------------------++--------------+---------+ | || hledger-1.21 | hledger | +====================================================++==============+=========+ | -f examples/10000x1000x10.journal print || 1.18 | 0.90 | | -f examples/10000x1000x10.journal register || 12.82 | 5.95 | | -f examples/10000x1000x10.journal balance || 1.38 | 0.86 | | -f examples/1000x1000x10.journal balance --weekly || 0.96 | 0.78 | | -f examples/10000x1000x10.journal balance --weekly || 13.07 | 10.79 | +----------------------------------------------------++--------------+---------+ -
ANSI color is now disabled automatically (on stdout) when the
-o/--output-fileoption is used (with a value other than-). (#1533) -
ANSI color is now also available in debug output, determined in the usual way by
--color,NO_COLOR, and whether the output (stderr) is interactive. -
The –version flag shows more details of the build, when known: git tag, number of commits since the tag, commit hash, platform and architecture. (Stephen Morgan)
-
balance: Capitalisation of “account” and “total” (and lack of a colon in the latter) in CSV output is now consistent for single- and multi-period reports.
-
balance reports’ CSV output now includes full account names. (#1566) (Stephen Morgan)
-
csv: We now accept spaces when parsing amounts from CSV. (Eric Mertens)
-
json: Avoid adding unnecessary decimal places in JSON output. (Don’t increase them all to 10 decimal places.) (Stephen Morgan)
-
json: Simplify amount precision (asprecision) in JSON output. It is now just the number of decimal places, rather than an object. (Stephen Morgan)
-
GHC 9.0 is now officially supported. GHC 8.0, 8.2, 8.4 are no longer supported; we now require GHC 8.6 or greater.
-
Added a now-required lower bound on containers. (#1514)
Fixes
-
Auto posting rules now match postings more precisely, respecting
cur:andamt:queries. (#1582) (Stephen Morgan) -
balance reports: Fix empty cells when amounts are too wide to fit (broken since 1.20) (#1526). (Stephen Morgan)
-
csv: Fix the escaping of double quotes in CSV output (broken in 1.21). (Stephen Morgan, [#1508])
-
register: Fix the running total when there is a report interval (broken since 1.19) (#1568). (Stephen Morgan)
-
stats: No longer gets confused by posting dates. (#772) (Stephen Morgan)
-
timeclock:
hledger printshows timeclock amounts with just 2 decimal places again (broken in 1.21). (#1527) -
When all transaction amounts have the same sign, the error message no longer adds an inferred price. (#1551) (Stephen Morgan)
-
Cleaned up some references to old man pages. (Felix Yan)
hledger-ui 1.22
Improvements
-
Don’t reset the
B/V(cost, value) state when reloading withgor--watch. (Stephen Morgan) -
The accounts screen is a little smarter at allocating space to columns. (Stephen Morgan)
-
Add support for the kakoune editor, and improve the invocations of some other editors. (crocket)
-
The
--versionflag shows more detail (git tag/patchlevel/commit hash, platform/architecture). (Stephen Morgan) -
GHC 9.0 is now officially supported. GHC 8.0, 8.2, 8.4 are no longer supported; we now require GHC 8.6 or greater.
-
Added a now-required lower bound on containers. (#1514)
Fixes
-
Queries in the register screen work again (broken in 1.21). (#1523) (Stephen Morgan)
-
Don’t write to
./debug.logwhen toggling value withV, or when reloading withgor--watchin the Transaction screen. (#1556) (Simon Michael, Stephen Morgan)
hledger-web 1.22
Improvements
-
The –version flag shows more detail (git tag/patchlevel/commit hash, platform/architecture). (Stephen Morgan)
-
Allow yesod-form 1.7 (Felix Yan)
-
Add now-required lower bound on containers. (#1514)
-
GHC 9.0 is now officially supported. GHC 8.0, 8.2, 8.4 are no longer supported; we now require GHC 8.6 or greater.
Fixes
- In the add form, fix a bug where extra posting rows were not added when needed in certain web browsers. (charukiewicz)
credits 1.22
This release was brought to you by Simon Michael, Stephen Morgan, Felix Yan, crocket, Eric Mertens, Damien Cassou, charukiewicz, and Garret McGraw.
2021-03-10 hledger-1.21
More speed; more cli-accessible docs; value change report; improvements to balance reports, valuation and more (announcement)
project-wide changes 1.21
-
roi has a new cookbook doc, and example files have been updated. (Dmitry Astapov)
-
Example CSV rules for the Daedalus wallet have been added.
-
The default stackage resolver/GHC version has been bumped to lts-17.4/ghc-8.10.4.
-
tools/generatejournal now includes more commodities and prices in generated journals. (Stephen Morgan)
-
Our functional tests now also run on BSD. (#1434, Felix Van der Jeugt)
-
Addon scripts in bin/ have been updated for latest hledger API (Stephen Morgan).
-
Addon scripts are now compiled as part of our CI tests, and always with the same version of hledger source they were shipped with. We now require script users to check out the hledger source tree and run the scripts (or,
bin/compile.sh) from there. This keeps users and tests in sync, making things more reliable for everyone. (#1453) -
Last but not least, hledger’s bash completions (provided in ./shell-completions/) have been thoroughly updated (#1404, #1410, Vladimir Zhelezov).
hledger cli 1.21
general
-
hledger is now generally about 10% more memory- and time-efficient, and significantly more so in certain cases, eg journals with many total transaction prices. (Stephen Morgan)
-
The
--help/-hand--versionflags are no longer position-sensitive; if there is a command argument, they now always refer to the command (where applicable). -
The new
--infoflag opens the hledger info manual, if “info” is in $PATH.hledger COMMAND --infowill open COMMAND’s info node. -
The
--manflag opens the hledger man page, if “man” is in $PATH.hledger COMMAND --manwill scroll the page to CMD’s section, if “less” is in $PATH. (We force the use of “less” in this case, overriding any $PAGER or $MAN_PAGER setting.) -
Some command aliases, considered deprecated, have been removed:
txns,equity, and the single-letter command aliasesa,b,p, andr. This was discussed at https://github.com/simonmichael/hledger/pull/1423 and on the hledger mail list. It might annoy some folks; please read the issue and do follow up there if needed. -
Notable documentation updates: the separate file format manuals have been merged into the hledger manual, the topic hierarchy has been simplified, the
balancecommand docs and “commands” section have been rewritten.
valuation
-
Costing and valuation are now independent, and can be combined.
--value=costand--value=cost,COMMare still supported (equivalent to--costand--cost --value=then,COMMrespectively), but deprecated. (Stephen Morgan) -
-Vis now always equivalent to--value=end. (Stephen Morgan) -
--value=endnow includes market price directives as well as transactions when choosing a valuation date for single-period reports. (#1405, Stephen Morgan) -
--value=endnow picks a consistent valuation date for single- and and multi-period reports. (#1424, Stephen Morgan) -
--value=thenis now supported with all reports, not just register. (Stephen Morgan) -
The too-vague
--infer-valueflag has been renamed to--infer-market-price. Tip: typing--infer-marketor even--inferis sufficient. The old spelling still works, but is now deprecated.
commands
-
add: Infix matches are now scored higher. If the search pattern occurs in full within the other description, that match gets a +0.5 score boost.
-
add:
--debugnow shows transaction matching results, useful when troubleshooting. -
balance: To accomodate new report types, the
--change|--cumulative|--historical|--budgetflags have been split into two groups: report type (--sum|--budget|...) and accumulation type (--change|--cumulative|--historical).--sumand--changeare the defaults, and your balance commands should still work as before. (Stephen Morgan et al, #1353) -
balance: The
--valuechangereport type has been added, showing the changes in period-end values. (Stephen Morgan, #1353) -
balance: With
--budget, the first and last subperiods are enlarged to whole intervals for calculating the budget goals also. (Stephen Morgan) -
balance: In multi-period balance reports, specifying a report period now also forces leading/trailing empty columns to be displayed, without having to add
-E. This is consistent withbalancesheetetc. (#1396, Stephen Morgan) -
balancesheet, cashflow: declaring just a Cash account no longer hides other Asset accounts.
-
check: Various improvements:
- check name arguments may be given as case-insensitive prefixes
accountsandcommoditiesmay also be specified as argumentsordereddatesnow checks each file separately (#1493)ordereddatesno longer supports the--uniqueflag or query argumentspayeesis a new check requiring payee declarationsuniqueleafnamesnow gives a fancy error message like the others- the old
checkdates/checkdupescommands have been dropped
-
help: The
helpcommand now shows only the hledger (CLI) manual, its--info/--man/--pagerflags have been renamed to-i/-m/-p, and--cathas been dropped. -
help: With a TOPIC argument (any heading or heading prefix, case insensitive), it will open the manual positioned at this topic if possible. (Similar to the new
--manand--infoflags described above.) -
payees: Add
--used/--declaredflags, like theaccountscommand. -
print: Now always shows amounts with all decimal places, unconstrained by commodity display style. This ensures more parseable and sensible-looking output in more cases, and behaves more like Ledger’s print. (There may be a cosmetic issue with trailing zeroes.) (#931, #1465)
-
print: With
--match, infix matches are now scored higher, as with the add command. -
print:
--matchnow provides debug output useful for troubleshooting.If you forget to give
--matchan argument, it can confusingly consume a following flag. Eg if you write:hledger print --match -x somebank # should be: hledger print --match=somebank -xit gets quietly parsed as:
hledger print --match="-x"Now you can at least use –debug to figure it out:
hledger print --match -x somebank --debug finding best match for description: "-x" similar transactions: ... -
roi: Now supports the valuation options (#1417, #1483), and uses commodity display styles. Also the manual has been simplified, with some content moved to the Cookbook. (Dmitry Astapov):
journal format
- The
commoditydirective now properly sets the display style of the no-symbol commodity. (#1461)
csv format
- More kinds of malformed signed numbers are now ignored, in particular just a sign without a number, which simplifies sign flipping with amount-in/amount-out.
hledger-ui 1.21
-
Register screen: also show transactions below the depth limit, as in 1.19, keeping the register balance in agreement with the balance shown on the accounts screen. This regressed in 1.20. (#1468)
-
Transaction screen: all decimal places are now shown. On the accounts screen and register screen we round amounts according to commodity display styles, but when you drill down to a transaction you probably want to see the unrounded amounts. (Like print, #cf #931.)
-
New flags
--manand--infoopen the man page or info manual. (See hledger)
hledger-web 1.21
-
Register: a date range can be selected by dragging over a region on the chart. (Arnout Engelen, #1471)
-
Add form: the description field’s autocompletions now also offer declared and used payee names.
-
New flags
--manand--infoopen the man page or info manual. (See hledger)
credits 1.21
This release was brought to you by Simon Michael, Vladimir Zhelezov, Stephen Morgan, Dmitry Astapov, Arnout Engelen, Damien Cassou, aragaer, Doug Goldstein, Caleb Maclennan, and Felix Van der Jeugt.
2021-01-29 hledger-1.20.4
- aregister: ignore a depth limit, as in 1.19 (#1468). In 1.20-1.20.3, aregister had stopped showing transactions in subaccounts below a depth limit. Now it properly shows all subaccount transactions, ensuring that the register’s final total matches a balance report with similar arguments.
2021-01-29 hledger-ui-1.20.4
- ui: register: show all txns in/under an account at the depth limit (#1468). In 1.20-1.20.3, the register screen had stopped showing transactions in accounts below a depth limit. Now it properly shows all subaccount transactions, even when there is a depth limit, ensuring that the register’s final total matches the balance shown on the account screen.
2021-01-29 hledger-web-1.20.4
- Use hledger 1.20.4.
2021-01-14 hledger 1.20.3, hledger-ui 1.20.3, hledger-web 1.20.3
-
When searching for price chains during valuation/currency conversion:
- It no longer hangs when there are price loops. (And in case of future bugs, it will give up rather than search forever.) (#1439)
- It now really finds the shortest path. (#1443)
- Useful progress info is displayed with
--debug=1or--debug=2.
-
balance, incomestatement: End-valued multi-period balance change reports (eg:
bal -MV) have been reverted to show value-of-change, as in previous hledger versions, rather than change-of-value, for now. (#1353, #1428) (Stephen Morgan) -
balance: End-valued balance change reports now choose the same final valuation date and show consistent results whether single-period or multi-period. (#1424) (Stephen Morgan)
-
balance: the
--dropoption now works withcsvandhtmloutput. (#1456) (Ilya Konovalov) -
check: the
commoditiescheck, and-s/--strictmode, now ignore the “AUTO” internal pseudo-commodity. (#1419) (Ilya Konovalov) -
register: Then-valued multi-period register reports (eg:
register -M --value=then) now calculate the correct values. (#1449) (Stephen Morgan) -
roi: now shows a better error message when required prices are missing. (#1446) (Dmitry Astapov)
-
The no-symbol commodity’s input number format can now be set by a
commoditydirective, like other commodities. (#1461)
2020-12-28 hledger 1.20.2
-
help: Fix loss of capitalisation in part of the hledger-ui manual.
-
help: Fix the node structure in info manuals.
-
Drop unused parsec dependency.
2020-12-28 hledger-ui 1.20.2
-
Fix loss of capitalisation in part of the manual.
-
Fix the info manual’s node structure.
2020-12-28 hledger-web 1.20.2
- Fix the info manual’s node structure.
2020-12-15 hledger 1.20.1
-
bal, bs, cf, is: In amount-sorted balance reports, equal-balance accounts are now reliably sorted by name. (Simon Michael, Stephen Morgan)
-
help: Fix the topic hierarchy in Info manuals.
2020-12-15 hledger-ui 1.20.1
-
Fix the F key (toggle future/forecast transactions), which in 1.20 would only work twice. (#1411)
-
Fix loss of forecasted transactions when the journal was reloaded while they were hidden. (#1204)
2020-12-06 hledger-web-1.20.1
- don’t hang when reloading the journal, eg after adding a transaction or editing the file. (#1409)
2020-12-05 hledger-1.20
Strict mode; check command; rendering, speed, and valuation fixes
project-wide changes 1.20
-
examples: clean up & add more budgeting examples; stripe csv
-
a hie.yaml file has been added, so hledger source loads easily in IDEs supporting haskell-language-server
-
The functional tests in tests/ have been moved into the respective packages, eg hledger/test/ and hledger-ui/test/.
-
Shake cabalfiles: now gives an error when it fails
-
make bench: add some large tabular reports; run just the slowest commands by default; run after make (func)test
hledger cli 1.20
general
-
strict mode: with -s/–strict, hledger requires that all accounts and commodities are declared with directives.
-
Reverted a stripAnsi change in 1.19.1 that caused a 3x slowdown of amount rendering in terminal reports. (#1350)
-
Amount and table rendering has been improved, so that stripAnsi is no longer needed. This speeds up amount rendering in the terminal, speeding up some reports by 10% or more since 1.19. (Stephen Morgan)
-
Amount eliding no longer displays corrupted ANSI codes (#1352, Stephen Morgan)
-
Eliding of multicommodity amounts now makes better use of available space, avoiding unnecessary eliding (showing as many amounts as possible within 32 characters). (Stephen Morgan)
-
Command line help for –no-elide now mentions that it also disables eliding of multicommodity amounts.
-
Query terms containing quotes (eg to match account names containing quotes) now work properly. (#1368, Stephen Morgan)
-
cli, journal: Date range parsing is more robust, fixing failing/incorrect cases such as: (Stephen Morgan)
- a hyphenated range with just years (
2017-2018) - a hyphenated date with no day in a hyphenated range (
2017-07-2018) - a dotted date with no day in a dotted range (
2017.07..2018.02)
- a hyphenated range with just years (
-
Debug output is prettier (eg, in colour), using pretty-simple instead of pretty-show.
-
csv, timedot, timeclock files now respect command line –alias options, like journal files. (#859)
-
Market price lookup for value reports is now more robust, fixing several bugs (and debug output is more informative). There has been a slight change in functionality: when chaining prices, we now prefer chains of all “forward” prices, even if longer, with chains involving reverse prices being the last resort. (#1402)
commands
-
add: number style (eg thousands separators) no longer disturbs the value that is offered as default. (#1378)
-
bal: –invert now affects -S/–sort-amount, reversing the order. (#1283, #1379) (Stephen Morgan)
-
bal: –budget reports no longer insert an extra space inside the brackets. (Stephen Morgan)
-
bal: –budget reports now support CSV output (#1155)
-
bal, is, bs –change: Valued multiperiod balance change reports now show changes of value, rather than the value of changes. (#1353, Stephen Morgan)
-
bal: clearer debug output, following debug levels policy
-
check: A new command which consolidating the various check-* commands. It runs the default, strict, or specified checks and produces no output and a zero exit code if all is well.
-
check-dates: this command is deprecated and will be removed in next release; use “hledger check ordereddates” instead.
-
check-dupes: this command is deprecated and will be removed in next release; use “hledger check uniqueleafnames” instead.
-
import: The journal’s commodity styles (declared or inferred) are now applied to imported amounts, overriding their original number format.
-
roi: TWR now handles same-day pnl changes and cashflows, calculation failure messages have been improved, and the documentation includes more detail and examples. (#1398) (Dmitry Astapov)
journal format
-
The journal’s commodity styles are now applied to forecasted transactions. (#1371)
-
journal, csv: commodity style is now inferred from the first amount, as documented, not the last. This was “working wrongly” since hledger 1.12..
-
A zero market price no longer causes “Ratio has zero denominator” error in valued reports. (#1373)
csv format
-
The new
decimal-markrule allows reliable number parsing when CSV numbers contain digit group marks (eg thousands separators). -
The CSV reader’s verbose “assignment” debug output is now at level 9.
hledger-ui 1.20
-
When entering a query with
/, malformed queries/regular expressions no longer cause the program to exit. (Stephen Morgan) -
Eliding of multicommodity amounts now makes better use of available space. (Stephen Morgan)
-
Enow parses theHLEDGER_UI_EDITORorEDITORenvironment variable correctly on Windows (ignoring the file extension), so if you have that set it should be better at opening your editor at the correct line. -
Enow supports positioning whenHLEDGER_UI_EDITORorEDITORis VS Code (“code”) (#1359) -
hledger-ui now has a (human-powered) test suite.
hledger-web 1.20
-
hledger-web’s test suite is re-enabled, now included in the main executable. hledger-web –test [– HSPECARGS] runs it.
-
Fix –forecast, broken in hledger-web since 1.18 (#1390)
-
Fix unescaped slashes in hledger-web description on hackage (TANIGUCHI Kohei)
-
The hledger-web version string is now provided at /version, as JSON (#1152)
-
The session file (hledger-web_client_session_key.aes) is now written in $XDG_DATA_DIR rather than the current directory. Eg on non-Windows systems this is ~/.cache/ by default (cf https://hackage.haskell.org/package/directory/docs/System-Directory.html#t:XdgDirectory). (#1344) (Félix Sipma)
credits 1.20
This release was brought to you by Simon Michael, Stephen Morgan, Dmitry Astapov, TANIGUCHI Kohei, legrostdg.
2020/09/07 hledger 1.19.1
hledger cli 1.19.1
-
Fix alignment of coloured numbers (#1345, #1349, Stephen Morgan)
-
Fix a regression in account type autodetection for accounts with capitalised names. (#1341)
-
Allow megaparsec 9
hledger-ui 1.19.1
- Allow megaparsec 9
hledger-web 1.19.1
-
Allow megaparsec 9
-
Drop redundant semigroups dependency (Felix Yan)
2020/09/01 hledger-1.19
New aregister and codes commands, more powerful CSV conditional rules, new sql output format, consistently default to flat mode, better colour control, cashflow report customisable like the others, more number/date/regexp parsing/validation, more speed.
hledger cli 1.19
new
-
aregister: a new command showing a transaction-oriented register for a single account. This is like hledger-ui, hledger-web, or your bank statement, and unlike the register command which shows individual postings possibly spanning multiple accounts. You might prefer aregister when reconciling real-world asset/liability accounts, and register when reviewing detailed revenues/expenses. (#1294)
-
codes: a new command for listing transaction codes
-
print: a new
sqloutput format has been added (Dmitry Astapov) -
A
--color/--colourcommand line option, support for theNO_COLORenvironment variable, and smarter autodetection of colour terminals have been added. (#1296) -
In queries, you can now use q or Q to specify a year quarter, like
2020q1orQ4. (#1247, Henning Thieleman, Stephen Morgan) -
When specifying report intervals, you can use
fortnightlyas a synonym forbiweekly. (Stephen Morgan)
improved
-
Reports involving multiple commodities now show at most two commodities per amount by default, making multicolumn reports less wide and more readable. Use the –no-elide flag to prevent this.
-
Flat (AKA list) mode is now the consistent default used by all balance reports and other commands showing accounts. (Stephen Morgan)
-
All commands supporting tree/list mode now accept -t and -l as short forms of the –tree and –flat flags. (#1286)
-
account,bal,bs,cf,is: –drop now also works in tree mode (Stephen Morgan)
-
bal,bs,cf,is: tabular balance reports now elide (compress) boring parent accounts, like the non-tabular reports. (Stephen Morgan)
-
bal,bs,cf,is: monthly column headings are no longer be displayed as just the month abbreviations, if multiple years are being displayed.
-
bal –budget: with –cumulative or –historical, column headings now correctly show the period end dates rather than date spans.
-
bs,cf,is: –no-total now hides subtotals as well as the grand total (Stephen Morgan)
-
bs,cf,is: -%/–percent no longer implies –no-total. (Stephen Morgan)
-
roi: errors are now shown without a call stack
-
tags: the new –parsed flag causes all tags or values to be shown, including duplicates, in the order they were parsed. Blank/empty values are omitted by default and can be shown with -E/–empty.
-
Debug output is now organised better by debug level. The levels are:
- normal command output only (no warnings)
- useful warnings & most common troubleshooting info (valuation, eg)
- common troubleshooting info, more detail
- report options selection
- report generation
- report generation, more detail
- input file reading
- input file reading, more detail
- command line parsing
- any other rarely needed or more in-depth info
correctness/robustness
-
Added a missing lower bound for aeson, making cabal installs more reliable. (#1268)
-
When parsing dates, we now require the year to have at least four digits. So eg Feb 1 in the year 10 would need to be written
0010-02-01, not10/02/01. would need to be written0200/1/1. This change was made for consistency and to avoid ambiguities; let us know if it causes you trouble. -
Command line options taking a numeric argument are now validated more carefully to avoid any issues with unexpected negatives or Int overflow. (Stephen Morgan)
-
Numbers with more than 255 decimal places, which we do not support, now give an error instead of silently misparsing. (#1326)
-
Digit groups in numbers are now limited to at most 255 digits each. (#1326)
-
Account aliases (on command line or in journal) containing a bad regular expression now give a more detailed error message.
-
In the argument of
amt:queries, whitespace around the operator, sign, or number no longer causes a parse error. (#1312) -
A tab character could get parsed as part of a commodity symbol, with confusing results. This no longer happens. (#1301, Dmitry Astapov)
-
add: fixed an error in the command line help (arguments are inputs, not a query)
journal format
-
account directives can specify a new account type,
Cash, for accounts which should be displayed in thecashflowreport.Cashaccounts are alsoAssetaccounts. -
Documentation of account types has been improved.
csv format
-
Conditional rule patterns can now be grouped with the
&(AND) operator, allowing more powerful matching. (Michael Sanders) -
“If tables”, a compact bulk format for conditional rules, have been added. (Dmitry Astapov)
-
csv conversion with a lot of conditional rules is now faster (Dmitry Astapov)
-
Invalid csv rules files now give clearer parse error messages. (Dmitry Astapov)
-
Inferring the appropriate default field separator based on file extension (, for .csv, ; for .ssv, \t for .tsv) now works as documented.
hledger-ui 1.19
-
A –color/–colour command line option, support for the NO_COLOR environment variable, and smarter autodetection of colour terminals have been added. (#1296)
-
-t and -l have been added as short forms of –tree and –flat command line flags.
-
Flat (AKA list) mode is now the default for the accounts screen.
-
t now toggles tree/list mode, while T sets the “today” period (#1286)
-
register screen: multicommodity amounts containing more than two commodities are now elided, unless the –no-elide flag is used.
-
register screen: a transaction dated outside the report period now is not shown even if it has postings dated inside the report period.
-
ESC now restores exactly the app’s state at startup, which includes clearing any report period limit. (#1286)
-
DEL/BS no longer changes the tree/list mode.
-
q now exits the help dialog, if active; press q again to exit the app. (#1286)
-
The help dialog’s layout is improved.
hledger-web 1.19
-
Added a missing lower bound for aeson, making cabal installs more reliable. (#1268)
-
Queries containing a malformed regular expression (eg the single character
?) now show a tidy error message instead “internal server error”. (Stephen Morgan, Simon Michael) (#1245) -
In account registers, a transaction dated outside the report period now is not shown even if it has postings dated inside the report period.
credits 1.19
This release was brought to you by Simon Michael, Stephen Morgan, Dmitry Astapov, Michael Sanders, Henning Thielemann, Martin Michlmayr, Colin Woodbury.
2020/06/21 hledger 1.18.1
hledger cli 1.18.1
-
value reports now work as in 1.17 again; inferring market prices from transactions is now an option, requiring the –infer-value flag. (#1239, #1253)
-
print: amounts in csv output now have commodity symbol, digit group separators and prices removed (Dmitry Astapov)
-
begin more systematic level usage in –debug output
-
journal: document recursive wildcards
hledger-ui 1.18.1
- Fix F key having no effect (#1255) (Dmitry Astapov)
2020/06/07 hledger 1.18
Fixed JSON output; market prices inferred from transactions; more Ledger file compatibility; more flexible journal entries from CSV; misc. fixes and improvements.
project-wide changes 1.18
-
new example scripts:
- hledger-combine-balances.hs, hledger-balance-as-budget.hs (Dmitry Astapov)
- hledger-check-tag-files.hs, hledger-check-tag-files2.hs
-
more CSV rule examples: coinbase, waveapp
-
new CI (continuous integration) system using Github Actions. Thanks to Travis and Appveyor for their service to date. Improvements:
- one CI service instead of several
- more closely integrated with code repo
- tests run on the three main platforms (linux, mac, windows)
- harmless commits are ignored automatically ([ci skip] no longer needed for doc commits)
- scheduled and on-demand testing (push to master, push to ci-* branches, pull request, weekly)
- now tested: all GHC versions, doctests, haddock building
- new shortcut url: https://ci.hledger.org
hledger cli 1.18
-
The –forecast flag now takes an optional argument (–forecast=PERIODICEXPR), allowing periodic transactions to start/end on any date and to overlap recorded transactions. (#835, #1236) (Dmitry Astapov)
-
An upper case file extension no longer confuses file format detection. (#1225)
-
In the commands list, redundant source scripts are now hidden properly when a corresponding .com/.exe file exists. (#1225)
-
We now show
..instead of-to indicate date ranges, eg in report titles, to stand out more from hyphenated dates. (Stephen Morgan) -
Period expressions (eg in -p, date:, and periodic rules) now accept
to,until,-, or..as synonyms. (Stephen Morgan) -
When parsing amounts, whitespace between sign and number is now allowed.
-
A clearer error message is shown on encountering a malformed regular expression.
commands
-
commands allowing different output formats now list their supported formats accurately in –help (#689)
-
commands allowing JSON output now actually produce JSON (#689)
-
bal, bs: show .. (not ,,) in report titles, like other reports
journal format
-
We now also infer market prices from transactions, like Ledger. See https://hledger.org/hledger.html#market-prices (#1239).
Upgrade note: this means value reports (-V, -X etc.) can give different output compared to hledger 1.17. If needed, you can prevent this by adding a P directive declaring the old price, on or after the date of the transaction causing the issue.
-
The include directive now accepts a file format prefix, like the -f/–file option. This works with glob patterns too, applying the prefix to each path. This can be useful when included files don’t have the standard file extension, eg:
include timedot:2020*.md -
We now accept (and ignore) Ledger-style lot dates (
[DATE]) and four lot price forms ({PRICE},{{PRICE}},{=PRICE},{{=PRICE}}), anywhere after the posting amount but before any balance assertion. -
We now accept Ledger-style parenthesised “virtual posting costs” (
(@),(@@)). In hledger these are equivalent to the unparenthesised form. -
The unbalanced transaction error message is clearer, especially when postings all have the same sign, and is split into multiple lines for readability.
csv format
-
You can now generate up to 99 postings in a transaction. (Vladimir Sorokin)
-
You can now generate postings with an explicit 0 amount. (#1112)
-
For each posting, when both numbered and unnumbered amount assignments are active (eg: both
amountandamount1), we ignore the unnumbered ones. This makes it easier to override oldamountrules. -
Fix a 1.17.1 regression involving amount-in/amount-out. (#1226)
-
Assigning too many non-zero or zero values to a posting amount now gives a clearer error. (#1226)
hledger-ui 1.18
- builds with hledger 1.18
hledger-web 1.18
-
The filter query is now preserved when clicking a different account in the sidebar. (Henning Thielemann)
-
Hyperlinks are now more robust when there are multiple journal files, eg links from register to journal now work properly. (#1041) (Henning Thielemann)
add form
-
Fixed a 2016 regression causing too many rows to be added by keypresses in the last amount field or CTRL-plus (#422, #1059).
-
Always start with four rows when opened.
-
Drop unneeded C-minus/C-plus keys & related help text.
credits 1.18
This release was brought to you by Simon Michael, Stephen Morgan, Dmitry Astapov, Henning Thielemann, Andriy Mykhaylyk, Pavan Rikhi, Vladimir Sorokin.
2020/03/01 hledger 1.17
CSV single-field matching; easier SSV/TSV conversion; fixed/enhanced close command; undo in add command; more JSON output; org headline support in timedot format; GHC 8.10 support.
project-wide changes 1.17
-
hledger-install tweaks
-
Simpler, clearer structure in the manuals and hledger.org sidebar.
-
A new Quick Start page
-
A new Common Tasks section in the hledger manual
-
A new Invoicing how-to
-
A basic example of rule parsing for the output of csb2format. (Evilham) csb2format deals with the CSB43/AEB43 format, which all banks operating in Spain must support.
hledger cli 1.17
-
hledger’s default date format is now ISO-8601 (YYYY-MM-DD). (Brian Wignall, Jakob Schöttl, Simon Michael)
-
Drop the file format auto-detection feature.
For a long time hledger has auto-detected the file format when it’s not known, eg when reading from a file with unusual extension (like .dat or .txt), or from standard input (-f-), or when using the include directive (which currently ignores file extensions). This was done by trying all readers until one succeeded. Recent changes to timedot format have made this unreliable. So now, hledger will no longer guess; when there’s no file extension or reader prefix available, it always assumes journal format. To specify one of the other formats, you must use its standard file extension (
.timeclock,.timedot,.csv,.ssv,.tsv), or a reader prefix (-f csv:foo.txt,-f timedot:-). Experimental, feedback welcome. -
More robust quoting of arguments for addons (#457). (Jacek Generowicz) Command lines like
hledger ui 'amt:>200'failed, because the process of dispatching fromhledgertohledger-uilost the quotes aroundamt:>20and the>character was interpreted as a shell redirection operator. -
–output-format now rejects invalid formats
-
Numbers in JSON output now provide a floating point Number representation as well as our native Decimal object representation, since the latter can sometimes contain 255-digit integers. The floating point numbers can have up to 10 decimal digits (and an unbounded number of integer digits.) Experimental, suggestions needed. (#1195)
-
Fix finding latest date in queryEndDate Or queries and simplify date comparison code. (Stephen Morgan)
-
Fix extra $ symbol (Mateus Furquim)
commands
-
add: you can use
<to undo and redo previous inputs (Gaith Hallak) -
bs, cf, is, bal, print, reg: support json output
-
bs, cf, is: fix excess subreport columns in csv output
-
bs, cf, is, bal: fix an issue with border intersections in –pretty-tables output. (Eric Mertens)
-
close: fix a rounding bug that could generate unbalanced transactions. (#1164)
-
close: hide cost prices by default, show them with –show-costs. close no longer preserves costs (transaction prices) unless you ask it to, since that can generate huge entries when there are many foreign currency/investment transactions. (#1165)
-
close: equity amounts are omitted by default, for simpler entries; -x/–explicit shows them (usually causing more postings). (#1165)
-
close: –interleaved generates equity postings alongside each closed account, making troubleshooting easier.
-
close: “equity:opening/closing balances” is now the default closing and opening account.
-
close: –close-desc/–open-desc customise the closing/opening transaction descriptions. (#1165)
-
close: some –open*/–close* flags have been simplified for memorability:
--closing -> --close --opening -> --open --close-to -> --close-acct --open-from -> --open-acctThe old flags are accepted as hidden aliases, and deprecated. (#1165)
-
print, register: a new valuation type, –value=then, shows the market value at each posting’s date.
-
print: -V/-X/–value now imply -x/–explicit, as -B/–cost does. This avoids a bug where print -V of a transaction with an implicit commodity conversion would convert only some of its postings to value.
journal format
-
The include directive no longer tries all readers. It now picks just one, based on the included file’s extension, defaulting to journal. (It doesn’t yet handle a reader prefix.)
-
The default commodity (
D) directive now limits display precision too, and is fully equivalent tocommoditydirectives for setting a commodity’s display style. (#1187)
csv format
-
Conditional blocks can now match single fields. \o/
-
The experimental –separator command line option has been dropped, replaced by a new
separatordirective in CSV rule files. (Aleksandar Dimitrov) -
The
.tsvand.ssvfile extensions are now recognised, and will set the defaultseparatorto TAB and semicolon respectively. (#1179) -
Manually assigning the “expenses:unknown” account name now works. (#1192)
-
CSV rule keywords are now case insensitive. (Aleksandar Dimitrov)
timeclock format
- Misc. fixes making parsing more robust. (Jakob Schöttl)
timedot format
- Org mode headlines (lines beginning with one or more
*followed by a space) can be used as date lines or timelog items (the stars are ignored). Also all org headlines before the first date line are ignored. This means org users can manage their timelog as an org outline (eg using org-mode/orgstruct-mode in Emacs), for organisation, faster navigation, controlling visibility etc. Experimental. - You can now write a description after a date, which will be used in all of that day’s transactions. Experimental.
hledger-ui 1.17
-
Don’t enable –auto by default.
-
Don’t enable –forecast by default; drop the –future flag. (#1193)
Previously, periodic transactions occurring today were always shown, in both “present” and “future” modes. To fix this, generation of periodic transactions and display of future transactions (all kinds) have been combined as “forecast mode”, which can be enabled with –forecast and/or toggled with the F key. The –future flag is now a hidden alias for –forecast, and deprecated.
hledger-web 1.17
-
Fonts have been improved on certain platforms. (David Zhang)
-
IPv6 is supported (Amarandus) (#1145)
-
The –host option can now take a local hostname (Amarandus) (#1145)
-
New –socket option to run hledger-web over an AF_UNIX socket file. (Carl Richard Theodor Schneider) This allows running multiple instances of hledger-web on the same system without having to manually choose a port for each instance, which is helpful for running individual instances for multiple users. In this scenario, the socket path is predictable, as it can be derived from the username.
-
The edit and upload forms now normalise line endings, avoiding parse errors (#1194). Summary of current behaviour:
-
hledger add and import commands will append with (at least some) unix line endings, possibly causing the file to have mixed line endings
-
hledger-web edit and upload forms will write the file with the current system’s native line endings, ie changing all line endings if the file previously used foreign line endings.
-
-
Numbers in JSON output now provide a floating point Number representation as well as our native Decimal object representation, since the latter can sometimes contain 255-digit integers. The floating point numbers can have up to 10 decimal digits (and an unbounded number of integer digits.) Experimental, suggestions needed. (#1195)
credits 1.17
This release was brought to you by Simon Michael, Aleksandar Dimitrov, Brian Wignall, Stephen Morgan, Jacek Generowicz, Gaith Hallak, Eric Mertens, Jakob Schöttl, Carl Richard Theodor Schneider, David Zhang, Amarandus, Evilham, Mateus Furquim and Rui Chen.
2019/12/01 hledger 1.16
GHC 8.8 support, much more powerful CSV conversion rules, percentage balance reports, misc improvements. (mail)
project-wide changes 1.16
-
add support for GHC 8.8, base-compat 0.11 (#1090)
-
drop support for GHC 7.10
-
add descriptions to most issue tracker labels
-
matrix.hledger.org now redirects to a more readable/useful url
hledger cli 1.16
-
The
--anonflag now also anonymises transaction codes and account names declared with account directives. (Mykola Orliuk) (#901) -
The benchmark suite has been disabled.
commands
-
balance/bs/cf/is: balance commands now support the
-%/--percentflag to show amounts as percentages of the column’s total. (Michael Kainer)If there are multiple commodities involved in a report hledger bails with an error message. This can be avoided by using
-B/--cost. Also note that if one uses -% with the balance command the chances are high that all numbers are 0. This is due to the fact that by default balance sums up to zero. If one wants to use -% in a meaningful way with balance one has to add a query.In order to keep the implementation as simple as possible
--treehas no influence over how the percentages are calculated, i.e., the percentages always represent the fraction of the columns total. If one wants to know the percentages relative to a parent account, one has to use a query to narrow down the accounts. -
balance:
--budgetno longer errors when there is neither budget nor transactions in the report period (Dmitry Astapov) -
balance:
--budgethas improved debug output (shows budget txns) (Dmitry Astapov) -
check-dates: now sets the exit status code (Amitai Burstein)
-
close: no longer strips zeroes after the decimal mark, and preserves parseable output (#1137)
-
close: the
--close-to,--open-fromoptions allow closing/opening account names to be chosen -
import: create the journal if missing, like the add command Streamlines import/migration instructions.
-
import:
--catchupmarks all transactions imported, without importing -
import: more informative output: mention the input files, also show a message when nothing was imported
-
prices: show price amounts with proper display style; always show full precision
-
roi: don’t give an error with empty input data (Dmitry Astapov)
-
tests: unit tests are now run by tasty, and show coloured output by default (#1090). Test running options have changed, see the command help. Some unit tests have been collapsed, so the reported test count has dropped a little.
journal format
- Fixed: wrong dates generated by certain periodic transaction rules, eg “~ every 12 months from 2019/04”. (Dmitry Astapov) (#1085)
csv format
CSV conversion is now more powerful (#1095, Dmitry Astapov, Simon Michael):
-
A variable number of postings can be generated, from zero to nine. (#627, #1095)
-
In conditional blocks,
skipcan be used to skip one or more records after a pattern match, or the newendrule can be used to skip all remaining records. (#1076) -
The new
balance-typeCSV rule controls which kind of balance assertions are generated (=, ==, =, ==) -
Postings with balance assignments can be generated. (#1000)
-
Both the amount-in/amount-out fields having a non-empty value is now accepted, as long as one of them is zero. (#570)
-
Line feeds/carriage returns in (quoted) CSV values are now converted to spaces during conversion. (#416, #841)
-
Field assignments can now unset a field (eg a posting can be suppressed by assigning no value to its account).
-
CSV records with varying lengths are now allowed; short records will be padded with empty fields as needed. This allows us to handle eg exported Google spreadsheets, where trailing empty fields are omitted.
-
Journals generated from CSV are now finalised and checked like ordinary journals (#1000). So invalid transactions generated from CSV will be rejected, amount styles will be standardised etc.
-
Fixed: we no longer add an extra (third) space between description and comment.
-
Fixed: whitespace on the line after an if block no longer causes misparsing. (#1120)
-
Fixed: an empty field assignment no longer consumes the next line. (#1001)
-
Fixed: interpolation of field names containing punctuation now works.
-
Docs have been rewritten and clarified.
Migration notes:
-
When
printing from CSV, there is now one less space between transaction descriptions and comments, which may generate noisy diffs if you are comparing old and new reports.diff -w(--ignore-all-space) will filter these out. -
CSV rules now give you more freedom to generate any journal entries you want, including malformed or unbalanced ones. The csv reader now checks the journal after conversion, so it will report any problems with the generated entries.
-
Balance assertions generated from CSV are not checked, currently. This is appropriate when you are downloading partial CSV data to be merged into your main journal. If you do need to check balance assertions right away, you can pipe through hledger again:
$ hledger -f a.csv print | hledger -f- print
hledger-ui 1.16
-
the B and V keys toggle cost or value display (like the
-Band-Vcommand line flags) -
uses hledger 1.16.1
hledger-web 1.16
-
The
--corsoption allows simple cross-origin requests to hledger-web (Alejandro García Montoro) -
Weeks in the add form’s date picker now start on Mondays (#1109) (Timofey Zakrevskiy)
-
No longer depends on json (#1190) or mtl-compat.
-
The test suite has been disabled for now.
credits 1.16
Release contributors: Simon Michael, Dmitry Astapov, Mykola Orliuk, Brian Wignall, Alejandro García Montoro, Timofey ZAKREVSKIY, Amitai Burstein, Michael Kainer.
2019/09/01 hledger 1.15
new website, faster and more flexible valuation, more accurate close command,
tags --values, new descriptions/payees/notes/diff commands, misc. fixes.
(mail)
project-wide changes 1.15
-
new unified website: hledger.org now has its own git repo, has absorbed the github wiki, and is generated with Sphinx.
-
hledger-api is now mothballed. Its functionality is included in hledger-web.
-
hledger-install.sh: bump to lts-14.4, hledger 1.15, drop hledger-api, now also works on FreeBSD 12.
-
Wine has been added to the list of install options.
-
Dmitry Astapov’s hledger docker image is now based on the “haskell” image.
-
Andreas Pauley’s hledger-makeitso has been renamed to hledger-flow.
-
bin/ addon scripts: hledger-swap-dates added; hledger-check, hledger-smooth updated. (#1072)
-
shell-completion scripts: updated
-
github: FUNDING.yml / sponsor button configured
-
tools: generatejournal updates: vary amount, make reports with fewer zeroes, start from a fixed year to keep tests stable, also generate P records. (#999)
-
tools: make, shake, CI: misc. updates
-
doc: add a README for the functional tests, linked from contrib guide
hledger cli 1.15
-
There is a new valuation option
--value=TYPE[,COMM], with backwards-compatible-B/--cost,-V/--market,-X/--exchange=COMMvariants. These provide control over valuation date (#329), and inference of indirect market prices (similar to Ledger’s-X) (#131). Experimental. -
Market valuation (
-V/-X/--value) is now much faster (#999):+-------------------------------------------++--------------+--------------+ | || hledger-1.14 | hledger-1.15 | +===========================================++==============+==============+ | -f examples/10000x1000x10.journal bal -Y || 2.43 | 2.44 | | -f examples/10000x1000x10.journal bal -YV || 44.91 | 6.48 | | -f examples/10000x1000x10.journal reg -Y || 4.60 | 4.15 | | -f examples/10000x1000x10.journal reg -YV || 61.09 | 7.21 | +-------------------------------------------++--------------+--------------+ -
How date options like
-Mand-pinteract has been updated and clarified. (Jakob Schöttl) (#1008, #1009, #1011) -
Restore
--aux-dateand--effectiveas--date2aliases (#1034). These Ledger-ish spellings were dropped over the years, to improve--help’s layout. Now we support them again, as semi-hidden flags (--helpdoesn’t list them, but they are mentioned in--date2’s help).
commands
-
add, web: on Windows, trying to add transactions to a file path containing trailing periods (eg
hledger add -f Documents.\.hledger.journal) now gives an error, since this could cause data loss otherwise (#1056). This affects the add command and hledger-web’s add form. -
bal:
--budget: don’t always convert to cost. -
bal:
--budget: don’t show a percentage when budgeted and actual amounts are in different commodities. -
bal/bs/bse:
-H/--historicalor--cumulativenow disables-T/--row-total(#329). Multiperiod balance reports which show end balances (eg,bal -MHorbs -M) no longer show a Totals column, since summing end balances generally doesn’t make sense. -
bs: show end date(s) in title, not transactions date span (#1078) Compound balance reports showing ending balances (eg balancesheet), now show the ending date (single column) or range of ending dates (multi column) in their title. ,, (double comma) is used rather than - (hyphen) to suggest a sequence of discrete dates rather than a continuous span.
-
close: preserve transaction prices (costs) accurately (#1035). The generated closing/opening transactions were collapsing/misreporting the costs in balances involving multiple costs. Now, each separately-priced amount gets its own posting. (And only the last of these (for each commodity) gets a balance assertion.) Also the equity posting’s amount is now always shown explicitly, which in multicommodity situations means that multiple equity postings are shown. The upshot is that a
balance -Breport will be unchanged after the closing & opening transactions generated by the close command. -
descriptions, payees, notes commands added (Caleb Maclennan)
-
diff: Gabriel Ebner’s hledger-diff is now a built in command, and https://github.com/gebner/hledger-diff is deprecated.
-
help: don’t require a journal file
-
print: now also canonicalises the display style of balance assertion amounts (#1042)
-
reg: show negative amounts in red, like balance and Ledger
-
reg: fix
--average, broken since 1.12 (#1003) -
stats: show count of market prices (P directives), and the commodities covered
-
tags: add
--valuesflag to list tag values. -
tags: now runs much faster when there many tags
journal format
- Transactions and postings generated/modified by periodic transaction
rules and/or transaction modifier rules are now marked with
generated-transaction,generated-posting, andmodifiedtags, for easier troubleshooting and filtering.
csv format
-
When interpolating CSV values, outer whitespace is now stripped. This removes a potential snag in amount field assignments (#1051), and hopefully is harmless and acceptable otherwise.
-
We no longer add inter-field spaces in CSV error messages, which was misleading and not valid RFC-4180 CSV format.
-
CSV parse errors are human-readable again (broken since 1.11) (#1038)
-
CSV rules now allow the amount to be left unassigned if there is an assignment to “balance”, which generates a balance assignment. (#1000)
hledger-ui 1.15
- uses hledger 1.15
hledger-web 1.15
-
--serve-apidisables the usual server-side web UI (leaving only the API routes) -
register page: account names are hyperlinked
-
?sidebar= now hides the sidebar, same as ?sidebar=0
-
fix “_create_locale could not be located” error on windows 7 (#1039)
-
uses hledger 1.15
credits 1.15
Release contributors: Simon Michael, Caleb Maclennan, Jakob Schöttl, Henning Thielemann, Dmitry Astapov, Ben Creasy, zieone, Boyd Kelly, Gabriel Ebner, Hans-Peter Deifel, Andreas Pauley.
2019/03/01 hledger 1.14
inclusive balance assertions, commodities command, --invert option,
JSON get/add support in hledger-web
(mail)
project-wide changes 1.14
-
hledger.org website: now uses https, home page updates, download page improved package list with status badges. Also the github wiki pages are now rendered as part of hledger.org, like the main site pages (with pandoc markdown and tables of contents). Building the site now requires that a copy of the wiki is checked out under wiki/.
-
bash completion support: removed duplicate options, added new options, stopped listing
-has a command, added some completion for external addon commands. -
release automation improvements
-
makefile cleanups; make site-liverender helps with local site preview
hledger cli 1.14
-
journal: subaccount-including balance assertions have been added, with syntax =* and ==* (experimental) (#290)
-
new commodities command lists commodity symbols
-
new
--invertoption flips sign of amounts in reports
hledger-ui 1.14
- use hledger 1.14
hledger-web 1.14
-
serve the same JSON-providing routes as in hledger-api:
/accountnames /transactions /prices /commodities /accounts /accounttransactions/ACCTAnd allow adding a new transaction by PUT’ing JSON (similar to the output of /transactions) to /add. This requires the
addcapability (which is enabled by default). Here’s how to test with curl:$ curl -s http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @in.json; echo(#316)
-
fix unbalanced transaction prevention in the add form
-
fix transaction-showing tooltips (#927)
-
manual updates: document
--capabilities/--capabilities-headerand editing/uploading/downloading. -
use hledger 1.14
hledger-api 1.14
- use hledger 1.14
hledger-lib 1.14
-
added:
transaction, [v]post*, balassert* constructors, for tests etc. -
renamed:
porigin -> poriginal -
refactored:
transaction balancing & balance assertion checking (#438)
credits 1.14
Release contributors: Simon Michael, Jakob Schöttl, Jakub Zárybnický.
2019/02/01 hledger 1.13
Unified command CLI help/manuals, bash completions, docker support,
improved budget report, --transpose, new account types syntax,
usability & bug fixes.
(mail)
project-wide changes 1.13
-
packaging: A docker image providing the main hledger tools is now linked on the download page. This is another way to get up-to-date hledger tools without building them yourself (and, a way to run hledger-ui on windows ?) (Dmitry Astapov, Simon Michael)
-
doc: fixed pandoc typography conversion in web manuals. Eg
--was being rendered as en-dash. (#954).
Developers:
-
developer docs have moved from the wiki into CONTRIBUTING.md (#920)
-
new streamlined changelog update process. Shake targets:
./Shake changelogs ./Shake CHANGES.md ./Shake CHANGES.md-dry ./Shake PKG/CHANGES.md ./Shake PKG/CHANGES.md-dryupdate the project-wide and/or package changelogs, inserting new commits (touching the respective directory, since the tag version or commit hash which is the first word in the changelog’s previous top heading) at the top, formatted as changelog entries.
-
./Shake PKG - builds a package plus its embedded docs. ./Shake build - builds all the packages and their embedded docs. (“stack build PKG” does not notice changes in embedded doc files.)
-
make ghci-shake - loads Shake.hs in ghci
-
make tags - includes doc source files, hpack/cabal files, Shake.hs
-
make site-livereload - opens a reloading browser view on the website html (requires
livereloadx) -
added a Dockerfile and helper scripts (Dmitry Astapov)
-
doc files and hpack/cabal files are included in TAGS again
hledger cli 1.13
-
cli: reorganised commands list. Addons now have a + prefix.
-
cli: the command line help and manual section for all hledger’s commands are now consistent, and generated from the same source.
-
cli: comprehensive bash completion support is now provided (in shell-completion/). See how-to in the Cookbook. (Jakob Schöttl)
-
balance
--budget: budget amounts now aggregate hierarchically, like account balances. Unbudgeted accounts can be shown with-E/--empty(along with zero-balance accounts), and the--show-budgetedflag has been dropped. (Dmitry Astapov) -
balance: new
--transposeflag switches the rows and columns of tabular balance reports (in txt and csv output formats). (Dmitry Astapov) -
close: generated balance assertions now have exact amounts with all decimal digits, ignoring display precision. Also, balance assertion amounts will no longer contain prices. (#941, #824, #958)
-
files: now shows up in the commands list
-
import: be silent when there’s nothing to import
-
roi: percentages smaller than 0.01% are displayed as zero (Dmitry Astapov)
-
stats, ui: correct file order is preserved when using
--auto(#949) -
journal: account directive: the account name can now be followed by a comment on the same line
-
journal: account directive: account types for the bs/bse/cf/is commands can now be set with a
type:tag, whose value isAsset,Liability,Equity,Revenue,Expense,A,L,E,RorX(case-insensitive). The previous syntax (account assets A) is now deprecated. -
journal: account directive: account sort codes like
account 1000(introduced in 1.9, deprecated in 1.11) are no longer supported. -
journal: transaction modifiers (auto postings) can affect periodic transactions (
--autocan add postings to transactions generated with--forecast). (Dmitry Astapov) -
journal: balance assertion errors now show exact amounts with all decimal digits. Previously it was possible, in case of a commodity directive limiting the display precision, to have a balance assertion error with asserted and actual amounts looking the same. (#941)
-
journal: fixed a periodic transaction parsing failure (#942) (Dmitry Astapov)
hledger-ui 1.13
-
on posix systems, control-z suspends the program
-
control-l now works everywhere and redraws more reliably
-
the top status info is clearer
-
use hledger 1.13
hledger-web 1.13
- use hledger 1.13
hledger-api 1.13
- use hledger 1.13
hledger-lib 1.13
-
in Journal’s jtxns field, forecasted txns are appended rather than prepended
-
API changes:
added: +setFullPrecision +setMinimalPrecision +expectParseStateOn +embedFileRelative +hereFileRelative
changed:
-
amultiplier -> aismultiplier
-
Amount fields reordered for clearer debug output
-
tpreceding_comment_lines -> tprecedingcomment, reordered
-
Hledger.Data.TransactionModifier.transactionModifierToFunction -> modifyTransactions
-
Hledger.Read.Common.applyTransactionModifiers -> Hledger.Data.Journal.journalModifyTransactions
-
HelpTemplate -> CommandDoc
-
credits 1.13
Release contributors: Simon Michael, Jakob Schöttl, Dmitry Astapov.
2018/12/02 hledger 1.12
Account type declarations, complete balance assertions, GHC 8.6 support, hledger-ui usability updates, misc fixes (mail)
hledger cli 1.12
-
install script: ensure a new-enough version of stack; more informative output
-
build with GHC 8.6/base-4.12 (Peter Simons)
-
add required upper bound for statistics (Samuel May)
-
--anonanonymises more thoroughly (including linked original postings) (Moritz Kiefer) -
unbalanced transaction errors now include location info (Mykola Orliuk)
-
accounts command:
--dropalso affects the default flat output, without needing an explicit--flatflag -
accounts command: the
--codesflag has been dropped -
accounts command: filtering by non-account-name queries now works
-
add command: fix transaction rendering regression during data entry and in journal file
-
balance command: fix wrongful eliding of zero-balance parent accounts in tree mode (Dmitry Astapov)
-
journal format, bs/bse/cf/is commands: account directives can declare account types (#877)
Previously you had to use one of the standard english account names (assets, liabilities..) for top-level accounts, if you wanted them to appear in the right place in the balancesheet, balancesheetequity, cashflow or incomestatement reports.Now you can use your preferred account names, and use account directives to declare which accounting class (Asset, Liability, Equity, Revenue or eXpense) an account (and its subaccounts) belongs to, by writing one of the letters A, L, E, R, X after the account name, after two or more spaces. This syntax may change (see issue). Experimental.
Currently we allow unlimited account type declarations anywhere in the account tree. So you could declare a liability account somewhere under assets, and maybe a revenue account under that, and another asset account even further down. In such cases you start to see oddities like accounts appearing in multiple places in a tree-mode report. I have left it this way for now in case it helps with, eg, modelling contra accounts, or combining multiple files each with their own account type declarations. (In that scenario, if we only allowed type declarations on top-level accounts, or only allowed a single account of each type, complications seem likely.)
-
journal format: periodic transaction rules now require a double space separator.
In periodic transaction rules which specify a transaction description or same-line transaction comment, this must be separated from the period expression by two or more spaces, to prevent ambiguous parsing. Eg this will parse correctly as “monthly” thanks to the double space:~ monthly In 2020 we'll end this monthly transaction. -
journal format: exact/complete balance assertions (Samuel May).
A stronger kind of balance assertion, written with a double equals sign, asserts an account’s complete account balance, not just the balance in one commodity. (But only if it is a single-commodity balance, for now.) Eg:1/1 (a) A 1 (a) B 1 (a) 0 = A 1 ; commodity A balance assertion, succeeds (a) 0 == A 1 ; complete balance assertion, fails -
journal format: account directives now allow whitespace or a comment after the account name
-
journal format: using ~ for home directory in include directives now works (#896) (Mykola Orliuk)
-
journal format: prevent misleading parse error messages with cyclic include directives (#853) (Alex Chen)
-
journal format: transaction modifier multipliers handle total-priced amounts correctly (#928).
Multipliers (*N) in transaction modifier rules did not multiply total-priced amounts properly. Now the total prices are also multiplied, keeping the transaction balanced. -
journal format: do amount inference/balance assignments/assertions before transaction modifiers (#893, #908) (Jesse Rosenthal)
Previously, transaction modifier (auto postings) rules were applied before missing amounts were inferred. This meant amount multipliers could generate too many missing-amount postings, making the transaction unbalanceable (#893).Now, missing amount inference (and balance assignments, and balance assertions, which are interdependent) are done earlier, before transaction modifier rules are applied (#900, #903).
Also, we now disallow the combination of balance assignments and transaction modifier rules which both affect the same account, which could otherwise cause confusing balance assertion failures (#912). (Because assignments now generate amounts to satisfy balance assertions before transaction modifier rules are applied (#908).)
-
journal format: periodic transaction rules are now aware of Y default year directives. (#892)
Ie when a default year Y is in effect, they resolve partial or relative dates using Y/1/1 as the reference date, rather than today’s date.
hledger-ui 1.12
-
fix “Any” build error with GHC < 8.4
-
error screen: always show error position properly (#904) (Mykola Orliuk)
-
accounts screen: show correct balances when there’s only periodic transactions
-
drop the
--status-togglesflag -
periodic transactions and transaction modifiers are always enabled.
Rule-based transactions and postings are always generated (--forecastand--autoare always on). Experimental. -
escape key resets to flat mode.
Flat mode is the default at startup. Probably it should reset to tree mode if--treewas used at startup. -
tree mode tweaks: add
--tree/-T/-Fflags, make flat mode the default,
toggle tree mode with T, ensure a visible effect on register screen -
hide future txns by default, add
--futureflag, toggle with F.
You may have transactions dated later than today, perhaps piped from print--forecastor recorded in the journal, which you don’t want to see except when forecasting.By default, we now hide future transactions, showing “today’s balance”. This can be toggled with the F key, which is easier than setting a date query.
--presentand--futureflags have been added to set the initial mode.(Experimental. Interactions with date queries have not been explored.)
-
quick help tweaks; try to show most useful info first
-
reorganise help dialog, fit content into 80x25 again
-
styling tweaks; cyan/blue -> white/yellow
-
less noisy styling in horizontal borders (#838)
-
register screen: positive amounts: green -> black
The green/red scheme helped distinguish the changes column from the black/red balance column, but the default green is hard to read on the pale background in some terminals. Also the changes column is non-bold now. -
use hledger 1.12
hledger-web 1.12
-
fix duplicate package.yaml keys warned about by hpack
-
use hledger 1.12
hledger-api 1.12
- use hledger 1.12
hledger-lib 1.12
-
switch to megaparsec 7 (Alex Chen)
We now track the stack of include files in Journal ourselves, since megaparsec dropped this feature. -
add ‘ExceptT’ layer to our parser monad again (Alex Chen)
This was removed under the assumption that it would be possible to write our parser without this capability. However, after a hairy backtracking bug, we would now prefer to have the option to prevent backtracking. -
more support for location-aware parse errors when re-parsing (Alex Chen)
-
make ‘includedirectivep’ an ‘ErroringJournalParser’ (Alex Chen)
-
drop Ord instance breaking GHC 8.6 build (Peter Simons)
-
flip the arguments of (divide|multiply)[Mixed]Amount
-
showTransaction: fix a case showing multiple missing amounts
showTransaction could sometimes hide the last posting’s amount even if one of the other posting amounts was already implcit, producing invalid transaction output. -
plog, plogAt: add missing newline
-
split up journalFinalise, reorder journal finalisation steps (#893) (Jesse Rosenthal)
ThejournalFinalisefunction has been split up, allowing more granular control. -
journalSetTime –> journalSetLastReadTime
-
journalSetFilePath has been removed, use journalAddFile instead
credits 1.12
Release contributors: Simon Michael, Alex Chen, Jesse Rosenthal, Samuel May, Mykola Orliuk, Peter Simons, Moritz Kiefer, Dmitry Astapov, Felix Yan, Aiken Cairncross, Nikhil Jha.
2018/9/30 hledger 1.11
Customisable account display order, support for other delimiter-separated formats (eg semicolon-separated), new files and roi commands, fixes (mail)
hledger cli 1.11
-
The default display order of accounts is now influenced by the order of account directives. Accounts declared by account directives are displayed first (top-most), in declaration order, followed by undeclared accounts in alphabetical order. Numeric account codes are no longer used, and are ignored and considered deprecated.
So if your accounts are displaying in a weird order after upgrading, and you want them alphabetical like before, just sort your account directives alphabetically.
-
Account sorting (by name, by declaration, by amount) is now more robust and supported consistently by all commands (accounts, balance, bs..) in all modes (tree & flat, tabular & non-tabular).
-
close: new
--opening/--closingflags to print only the opening or closing transaction -
files: a new command to list included files
-
prices: query arguments are now supported. Prices can be filtered by date, and postings providing transaction prices can also be filtered.
-
rewrite: help clarifies relation to print
--auto(#745) -
roi: a new command to compute return on investment, based on hledger-irr
-
test: has more verbose output, more informative failure messages, and no longer tries to read the journal
-
csv: We use a more robust CSV lib (cassava) and now support non-comma separators, eg
--separator ';'(experimental, this flag will probably become a CSV rule) (#829) -
csv: interpolated field names in values are now properly case insensitive, so this works:
fields …,Transaction_Date,… date %Transaction_Date
-
journal: D (default commodity) directives no longer break multiplier amounts in transaction modifiers (AKA automated postings) (#860)
-
journal: “Automated Postings” have been renamed to “Transaction Modifiers”.
-
journal: transaction comments in transaction modifier rules are now parsed correctly. (#745)
-
journal: when include files form a cycle, we give an error instead of hanging.
-
upper-case day/month names in period expressions no longer give an error (#847, #852)
hledger-ui 1.11
- uses hledger-lib 1.11
hledger-web 1.11
- uses hledger-lib 1.11
hledger-api 1.11
- uses hledger-lib 1.11
hledger-lib 1.11
-
compilation now works when locale is unset (#849)
-
all unit tests have been converted from HUnit+test-framework to easytest
-
doctests now run quicker by default, by skipping reloading between tests. This can be disabled by passing
--slowto the doctests test suite executable. -
doctests test suite executable now supports
--verbose, which shows progress output as tests are run if doctest 0.16.0+ is installed (and hopefully is harmless otherwise). -
doctests now support file pattern arguments, provide more informative output. Limiting to just the file(s) you’re interested can make doctest start much quicker. With one big caveat: you can limit the starting files, but it always imports and tests all other local files those import.
-
a bunch of custom Show instances have been replaced with defaults, for easier troubleshooting. These were sometimes obscuring important details, eg in test failure output. Our new policy is: stick with default derived Show instances as far as possible, but when necessary adjust them to valid haskell syntax so pretty-show can pretty-print them (eg when they contain Day values, cf https://github.com/haskell/time/issues/101). By convention, when fields are shown in less than full detail, and/or in double-quoted pseudo syntax, we show a double period (..) in the output.
-
Amount has a new Show instance. Amount’s show instance hid important details by default, and showing more details required increasing the debug level, which was inconvenient. Now it has a single show instance which shows more information, is fairly compact, and is pretty-printable.
ghci> usd 1 OLD: Amount {acommodity=“$”, aquantity=1.00, ..} NEW: Amount {acommodity = “$”, aquantity = 1.00, aprice = NoPrice, astyle = AmountStyle “L False 2 Just ‘.’ Nothing..”, amultiplier = False}
MixedAmount’s show instance is unchanged, but showMixedAmountDebug is affected by this change:
ghci> putStrLn $ showMixedAmountDebug $ Mixed [usd 1] OLD: Mixed [Amount {acommodity=“$”, aquantity=1.00, aprice=, astyle=AmountStyle {ascommodityside = L, ascommodityspaced = False, asprecision = 2, asdecimalpoint = Just ‘.’, asdigitgroups = Nothing}}] NEW: Mixed [Amount {acommodity=“$”, aquantity=1.00, aprice=, astyle=AmountStyle “L False 2 Just ‘.’ Nothing..”}]
-
Same-line & next-line comments of transactions, postings, etc. are now parsed a bit more precisely (followingcommentp). Previously, parsing no comment gave the same result as an empty comment (a single newline); now it gives an empty string.
Also, and perhaps as a consequence of the above, when there’s no same-line comment but there is a next-line comment, we’ll insert an empty first line, since otherwise next-line comments would get moved up to the same line when rendered. -
Hledger.Utils.Test exports HasCallStack
-
queryDateSpan, queryDateSpan’ now intersect date AND’ed date spans instead of unioning them, and docs are clearer.
-
pushAccount -> pushDeclaredAccount
-
jaccounts -> jdeclaredaccounts
-
AutoTransaction.hs -> PeriodicTransaction.hs & TransactionModifier.hs
-
Hledger.Utils.Debug helpers have been renamed/cleaned up
credits 1.11
Release contributors: Simon Michael, Joseph Weston, Dmitry Astapov, Gaith Hallak, Jakub Zárybnický, Luca Molteni, SpicyCat.
2018/6/30 hledger 1.10
hledger-web edit/upload/download and permissions, more expressive periodic transactions, more informative parse errors, misc fixes (mail)
project-wide changes 1.10
-
build cleanly with all supported GHC versions again (7.10 to 8.4)
-
support latest deps
-
back in Stackage LTS (12.0)
hledger-lib 1.10
-
extensive refactoring and cleanup of parsers and related types and utilities
-
readJournalFile(s) cleanup, these now use InputOpts
-
doctests now run a bit faster (#802)
hledger cli 1.10
-
journal: many parse error messages have become more informative, and some now show the source line and error location.
-
journal:
;tag:is no longer parsed as a tag named “;tag” (#655) -
journal: transaction price amounts having their own price amounts is now a parse error
-
journal: amounts with space as digit group separator and trailing whitespace now parse correctly (#780)
-
journal: in amounts containing digits and a single space, the space is now interpreted as a digit group separator, not a decimal separator (#749)
-
journal: in commodity/format/D directives, the amount must now include a decimal separator.
When more precise control is needed over number parsing, our recommended solution is commodity directives. Commodity directives that don’t specify the decimal separator leave things ambiguous, increasing the chance of misparsing numbers. In some cases it could cause amounts with a decimal point to be parsed as if with a digit group separator, so 1.234 became 1234.
It seems the simple and really only way to do this reliably is to require an explicit decimal point character. Most folks probably do this already. Unfortunately, it makes another potential incompatiblity with ledger and beancount journals. But the error message will be clear and easy to work around.
-
journal: directives currently have diverse and somewhat tricky semantics, especially with multiple files. The manual now describes their behaviour precisely.
-
journal:
aliasandapply accountdirectives now affectaccountdirectives (#825) -
journal: periodic transactions can now have all the usual transaction fields (status mark, code, description, comment), for generating more expressive forecast transactions.
-
journal: forecast transactions now have the generating period expression attached as a tag named “recur”.
-
journal: periodic transactions now start on the first instance of the recurring date, rather than the day after the last regular transaction (#750)
-
journal: periodic transaction rules now allow period expressions relative to today’s date
-
csv: amount-in/amount-out errors are more detailed
-
balance:
--dropis now ignored when not in flat mode, rather than producing a corrupted report (#754) -
budget:
--dropnow preserves the<unbudgeted>top-level account in--budgetreports -
register: in CSV output, the code field is now included (#746)
-
smart dates now allow the YYYYMM format, and are better documented
-
uses hledger-lib 1.10
hledger-ui 1.10
-
the effect of
--value,--forecast, and--anonflags is now preserved on reload (#753) -
edit-at-transaction-position is now also supported when $EDITOR is neovim
-
support/require fsnotify 0.3.0.1+
-
uses hledger-lib 1.10
hledger-web 1.10
-
view, add, edit permissions can be set at CLI or by Sandstorm HTTP header
-
the edit form has been revived, for whole-journal editing
-
the journal can now be uploaded and downloaded
-
the e key toggles empty accounts in the sidebar
-
multiple
-foptions, and--auto, work again -
uses hledger-lib 1.10
hledger-api 1.10
- uses hledger-lib 1.10
credits 1.10
Release contributors: Simon Michael, Alex Chen, Everett Hildenbrandt, Jakub Zárybnický, Nolan Darilek, Dmitry Astapov, Jacob Weisz, Peter Simons, Stephen Morgan, Pavlo Kerestey, Trevor Riles, Léo Gaspard, Mykola Orliuk, Wad, Nana Amfo.
2018/3/31 hledger 1.9
Report cleanups, normal-positive reports, HTML output, account sort codes, budget improvements. (mail)
Release contributors: Simon Michael, Eli Flanagan, Peter Simons, Christoph Nicolai, agander, M Parker, Moritz Kiefer, Mykola Orliuk.
- support ghc 8.4, latest deps
hledger-lib 1.9
-
when the system text encoding is UTF-8, ignore any UTF-8 BOM prefix found when reading files.
-
CompoundBalanceReport amounts are now normally positive. (experimental)
hledger cli 1.9
-
journal: account directives can define a numeric account code to customize sorting. bal/bs/cf/is will sort accounts by account code, if any, then account name.
-
csv: reading a CSV file containing no records is no longer an error
-
cli: when the system text encoding is UTF-8, ignore any UTF-8 BOM prefix found when reading files. (Paypal’s new CSV has this BOM prefix, causing a confusing parse error.)
-
cli: tabular reports no longer have a trailing blank line added. (This allows omitting the “>=0” delimiters in our functional tests, making them easier to read and maintain.)
-
acc: the accounts command now has
--declaredand--usedflags -
bal: the
--invertflag flips all signs -
bal:
--dropnow works with CSV output -
bal/bs/bse/cf/is: show overall report span in title
-
bal/bs/bse/cf/is: show short month names as headings in monthly reports
-
bal/bs/bse/cf/is: these commands can now generate HTML output
-
bal/bs/is/cf: drop short name and indent fields from multicolumn CSV
-
bs/bse/cf/is: these, the “financial statement” commands, now show normal income, liability and equity balances as positive numbers. Negative numbers now indicate a contra-balance (eg an overdrawn checking account), a net loss, or a negative net worth. This makes these reports more like conventional financial statements, and easier to read and share with others. (Other commands, like balance, have not changed.) (experimental)
-
bs/cf/is: always show a tabular report, even with no report interval. Previously you would get a simple borderless report like the original balance command. Less code, fewer bugs.
-
bs/bse/cf/is: in CSV output, don’t repeat the headings row for each subreport
-
budget: warn that CSV output with bal
--budgetis unimplemented -
budget: bal
--budgetshows budget goals even with no or zero actual amounts. Makes budget reports more intuitive, at the cost of a temporary hack which may misorder columns in some cases (if actual and budget activity occur in a different range of columns). -
budget:
--budgetuses only periodic txns with the selected interval.
Budgets with different interval, eg a daily and weekly budget, are independent. -
budget: show mostly fixed-width columns for readability
-
budget: fix bug where a budget report could include budget goals ending on the day before the report start date (splitSpan issue)
-
close: the equity command has been renamed to close. It now ignores any begin date (it always closes historical end balances). It also ignores
--date2.
hledger-ui 1.9
-E/--emptytoggles zeroes at startup (with opposite default to cli)
hledger-web 1.9
-E/--emptytoggles zeroes at startup (with opposite default to cli)
hledger-api 1.9
2017/12/31 hledger 1.5
(mail)
Release contributors: Simon Michael, Dmitry Astapov, Mykola Orliuk, Eli Flanagan, Elijah Caine, Sam Jeeves, Matthias Kauer, Hans-Peter Deifel, Mick Dekkers, Nadrieril, Alvaro Fernando García.
project-wide changes 1.5
- remove upper bounds on all but hledger* and base (experimental) It’s rare that my deps break their api or that newer versions must be avoided, and very common that they release new versions which I must tediously and promptly test and release hackage revisions for or risk falling out of stackage. Trying it this way for a bit.
hledger-lib 1.5
-
-V/--valueuses today’s market prices by default, not those of last transaction date. #683, #648) -
csv: allow balance assignment (balance assertion only, no amount) in csv records (Nadrieril)
-
journal: allow space as digit group separator character, #330 (Mykola Orliuk)
-
journal: balance assertion errors now show line of failed assertion posting, #481 (Sam Jeeves)
-
journal: better errors for directives, #402 (Mykola Orliuk)
-
journal: better errors for included files, #660 (Mykola Orliuk)
-
journal: commodity directives in parent files are inherited by included files, #487 (Mykola Orliuk)
-
journal: commodity directives limits precision even after
-B, #509 (Mykola Orliuk) -
journal: decimal point/digit group separator chars are now inferred from an applicable commodity directive or default commodity directive. #399, #487 (Mykola Orliuk)
-
journal: numbers are parsed more strictly (Mykola Orliuk)
-
journal: support Ledger-style automated postings, enabled with
--autoflag (Dmitry Astapov) -
journal: support Ledger-style periodic transactions, enabled with
--forecastflag (Dmitry Astapov) -
period expressions: fix “nth day of {week,month}”, which could generate wrong intervals (Dmitry Astapov)
-
period expressions: month names are now case-insensitive (Dmitry Astapov)
-
period expressions: stricter checking for invalid expressions (Mykola Orliuk)
-
period expressions: support “every 11th Nov” (Dmitry Astapov)
-
period expressions: support “every 2nd Thursday of month” (Dmitry Astapov)
-
period expressions: support “every Tuesday”, short for
every <n>th day of week(Dmitry Astapov)
hledger cli 1.5
-
--autoadds Ledger-style automated postings to transactions (Dmitry Astapov, Mykola Orliuk) -
--forecastgenerates Ledger-style periodic transactions in the future (Dmitry Astapov, Mykola Orliuk) -
-V/--valueuses today’s market prices by default, not those of last transaction date. #683, #648 -
add: suggest implied (parent) and declared (by account directives) account names also
-
bal:
--budgetshows performance compared to budget goals defined with periodic transactions. Accounts with budget goals are displayed folded (depth-clipped) at a depth matching the budget specification. Unbudgeted accounts are hidden, or with--show-unbudgeted, shown at their usual depth. (Dmitry Astapov) -
import: the output of
--dry-runis now valid journal format -
print:
-Bshows converted amounts again, as in 1.1, even without-x. #551 (Mykola Orliuk, Simon Michael) -
tag: the first argument now filters tag names, additional arguments filter transactions (#261)
hledger-ui 1.5
-
fix help -> view manual (on posix platforms) #623
-
support
-V/--value,--forecast,--auto
hledger-web 1.5
-
add form account fields now suggest implied and declared account names also
-
add form date field now uses a datepicker (Eli Flanagan)
-
don’t write a session file at startup, don’t require a writable working directory
-
support
-V/--value, –forecast,--auto
hledger-api 1.5
2017/9/30 hledger 1.4
easy install script, simpler help commands, experimental addon commands now built in, new balancesheetequity/tags commands, new import command for easy CSV merging, print can detect new transactions, balance reports can sort by amount, cli conveniences (mail)
Release contributors: Simon Michael, Nicholas Niro, Hans-Peter Deifel, Jakub Zárybnický, Felix Yan, Mark Hansen, Christian G. Warden, Nissar Chababy, Peter Simons.
-
update stack configs for the last three GHC versions, add “make test-stackage” for finding stackage build problems, switch to GHC 8.2.1 as default for developer builds
-
streamline docs page
-
improve changelog/release notes process
-
improve makefile help and speed
-
Added a new installer script for the hledger tools, which aims to dodge common pitfalls and just work. Based on the stack install script, this bash script is cross platform, uses cabal or stack, installs stack and GHC if needed, and installs the latest release of all major hledger packages. See http://hledger.org/download for details.
hledger-lib 1.4
-
add readJournalFile[s]WithOpts, with simpler arguments and support for detecting new transactions since the last read.
-
query: add payee: and note: query terms, improve description/payee/note docs (Jakub Zárybnický, Simon Michael, #598, #608)
-
journal, cli: make trailing whitespace significant in regex account aliases Trailing whitespace in the replacement part of a regular expression account alias is now significant. Eg, converting a parent account to just an account name prefix:
--alias '/:acct:/=:acct ' -
timedot: allow a quantity of seconds, minutes, days, weeks, months or years to be logged as Ns, Nm, Nd, Nw, Nmo, Ny
-
csv: switch the order of generated postings, so account1 is first. This simplifies things and facilitates future improvements.
-
csv: show the “creating/using rules file” message only with
--debug -
csv: fix multiple includes in one rules file
-
csv: add “newest-first” rule for more robust same-day ordering
-
deps: allow ansi-terminal 0.7
-
deps: add missing parsec lower bound, possibly related to #596, fpco/stackage#2835
-
deps: drop oldtime flag, require time 1.5+
-
deps: remove ghc < 7.6 support, remove obsolete CPP conditionals
-
deps: fix test suite with ghc 8.2
-
Fix a bug with
-Hshowing nothing for empty periods (#583, Nicholas Niro) This patch fixes a bug that happened when using the-Hoption on a period without any transaction. Previously, the behavior was no output at all even though it should have shown the previous ending balances of past transactions. (This is similar to previously using-Hwith-E, but with the extra advantage of not showing empty accounts) -
allow megaparsec 6 (#594)
-
allow megaparsec-6.1 (Hans-Peter Deifel)
-
fix test suite with Cabal 2 (#596)
hledger cli 1.4
-
cli: a @FILE argument reads flags & args from FILE, one per line
-
cli: reorganized commands list, added some new command aliases: accounts: a balance: b print: p, txns register: r
-
cli: accept
-NUMas a shortcut for--depth=NUM(eg: -2) -
cli: improve command-line help for
--date2(#604) -
cli: make
--helpand-hthe same, drop--manand--infofor now (#579) -
help: offers multiple formats, accepts topic substrings. The separate info/man commands have been dropped. help now chooses an appropriate documentation format as follows:
- it uses info if available,
- otherwise man if available,
- otherwise $PAGER if defined,
- otherwise less if available,
- otherwise it prints on stdout
- (and it always prints on stdout when piped).
You can override this with the
--info/--man/--pager/--catflags. (#579)
-
bal/bs/cf/is:
--sort-amount/-Ssorts by largest amount instead of account name -
bs/cf/is: support
--output-fileand--output-format=txt|csvThe CSV output should be reasonably ok for dragging into a spreadsheet and reformatting. -
bal/bs/cf/is: consistent double space between columns, consistent single final blank line. Previously, amounts wider than the column headings would be separated by only a single space.
-
bs/is: don’t let an empty subreport disable the grand totals (fixes #588)
-
cf: exclude asset accounts with “:fixed” in their name (Christian G. Warden, Simon Michael, #584)
-
new balancesheetequity command: like balancesheet but also shows equity accounts (Nicholas Niro)
-
new import command: adds new transactions seen in one or more input files to the main journal file
-
print:
--newshows only transactions added since last time (saves state in .latest.JOURNALFILE file) -
new tags command: lists tags in matched transactions
-
most addons formerly shipped in bin/ are now builtin commands. These include: check-dates, check-dupes, equity, prices, print-unique, register-match, rewrite.
-
refactor: new Commands module and subdirectory. Builtin commands are now gathered more tightly in a single module, Hledger.Cli.Commands, facilitating change. The legacy “convert” command has been dropped.
-
refactor: BalanceView -> CompoundBalanceCommand
-
deps: drop support for directory < 1.2
-
deps: allow ansi-terminal 0.7
-
deps: drop oldtime flag, require time 1.5+
-
deps: simplify shakespeare bounds
-
deps: remove ghc < 7.6 support
-
bs/is: don’t let an empty subreport disable the grand totals (#588)
-
allow megaparsec 6 (#594)
-
allow megaparsec-6.1 (Hans-Peter Deifel)
-
restore upper bounds on hledger packages
hledger-ui 1.4
-
a @FILE argument reads flags & args from FILE, one per line
-
enable
--pivotand--anonoptions, like hledger CLI (#474) (Jakub Zárybnický) -
accept
-NUMas a shortcut for--depth NUM -
deps: allow ansi-terminal 0.7
-
deps: drop oldtime flag, require time 1.5+
-
allow megaparsec 6 (#594, Simon Michael, Hans-Peter Deifel)
-
allow megaparsec-6.1 (Hans-Peter Deifel)
-
allow vty 5.17 (Felix Yan)
-
allow brick 0.24
-
restore upper bounds on hledger packages
hledger-web 1.4
-
a @FILE argument reads flags & args from FILE, one per line
-
enable
--pivotand--anonoptions, like hledger CLI (#474) (Jakub Zárybnický) -
web: Make “Add transaction” button tabbable (#430) (Jakub Zárybnický)
-
accept
-NUMas a shortcut for--depth NUM -
deps: drop oldtime flag, require time 1.5+, remove ghc < 7.6 support
- remove unnecessary bound to satisfy hackage server
-
allow megaparsec 6 (#594, Simon Michael, Hans-Peter Deifel)
-
allow megaparsec-6.1 (Hans-Peter Deifel)
-
restore upper bounds on hledger packages
hledger-api 1.4
- api: add support for swagger2 2.1.5+ (fixes #612)
-
require servant-server 0.10+ to fix compilation warning
-
restore upper bounds on hledger packages
2017/6/30 hledger 1.3
terminology/UI improvements for the status field, selection/scrolling/movement improvements in hledger-ui, negative amounts shown in red, bugfixes. (mail)
Release contributors: Simon Michael, Mykola Orliuk, Christian G. Warden, Dmitry Astapov, Justin Le, Joe Horsnell, Nicolas Wavrant, afarrow, Carel Fellinger, flip111, David Reaver, Felix Yan, Nissar Chababy, Jan Zerebecki.
Tools
make ghci-prof starts GHCI in profiling mode, enabling stack traces with traceStack
make ghci-web now also creates required symlinks
make site-reload opens an auto-reloading browser on the latest site html
make changelog-draft shows the commits since last tag as org nodes
hledger-lib 1.3
journal format
The “uncleared” transaction/posting status (and associated UI flags and keys) has been renamed to “unmarked” to remove ambiguity and confusion. See the issue and linked mail list discussion for more background. (#564)
csv format
In CSV conversion rules, assigning to the “balance” field name creates balance assertions (#537, Dmitry Astapov).
Doubled minus signs are handled more robustly (fixes #524, Nicolas Wavrant, Simon Michael)
Misc
Multiple status: query terms are now OR’d together. (#564)
Deps: allow megaparsec 5.3.
hledger cli 1.3
CLI
The “uncleared” transaction/posting status, and associated UI flags
and keys, have been renamed to “unmarked” to remove ambiguity and
confusion. This means that we have dropped the --uncleared flag,
and our -U flag now matches only unmarked things and not pending
ones. See the issue and linked mail list discussion for more
background. (#564)
Also the -P short flag has been added for --pending, and the -U/-P/-C
flags can be combined.
bs/is: fix “Ratio has zero denominator” error (#535)
bs/is/cf: fix --flat (#552) (Justin Le, Simon Michael)
bal/bs/is/cf: show negative amounts in red (Simon Michael, Justin Le). These commands now show negative amounts in red, when hledger detects that ANSI codes are supported, (ie when TERM is not “dumb” and stdout is not being redirected or piped).
print: show pending mark on postings (fixes #563). A pending mark on postings is now displayed, just like a cleared mark. Also there will now be a space between the mark and account name.
print: amounts are now better aligned, eg when there are posting status marks or virtual postings.
Addons
prices: add --inverted-costs flag, sort output, increase precision
(Mykola Orliuk)
rewrite: add support for rewriting multipler postings into different commodities. For example, postings in hours can be used to generate postings in USD. (#557) (Christian G. Warden)
make addons compiles the experimental add-ons.
hledger-ui 1.3
The register screen now shows transaction status marks.
The “uncleared” status, and associated UI flags and keys, have been
renamed to “unmarked” to remove ambiguity and confusion. This means
that we have dropped the --uncleared flag, and our -U flag now
matches only unmarked things and not pending ones. See the issue and
linked mail list discussion for more background. (#564)
The P key toggles pending mode, consistent with U (unmarked) and C
(cleared). There is also a temporary --status-toggles flag for testing
other toggle styles; see hledger-ui -h. (#564)
There is now less “warping” of selection when lists change:
-
When the selected account disappears, eg when toggling zero accounts, the selection moves to the alphabetically preceding item, instead of the first one.
-
When the selected transaction disappears, eg when toggling status filters, the selection moves to the nearest transaction by date (and if several have the same date, by journal order), instead of the last one.
In the accounts and register screens, you can now scroll down further so that the last item need not always be shown at the bottom of the screen. And we now try to show the selected item centered in the following situations:
- after moving to the end with Page down/End
- after toggling filters/display modes (status, real, historical..)
- on pressing the control-l key (this forces a screen redraw, also)
- on entering the register screen from the accounts screen (except the first time, a known problem).
Items near the top won’t be centered because we don’t scroll above the top of the list.
Emacs movement keys are now supported, as well as VI keys.
CTRL-b/CTRL-f/CTRL-n/CTRL-p and hjkl should work wherever unmodified arrow keys work.
In the transaction screen, amounts are now better aligned, eg when there are posting status marks or virtual postings.
Deps: allow brick 0.19 (#575, Felix Yan, Simon Michael)
hledger-web 1.3
Depends on hledger 1.3.
hledger-api 1.3
Depends on hledger 1.3.
2017/3/31 hledger 1.2
new commands list,
more powerful balancesheet/incomestatement/cashflow commands,
more parseable print output,
better --pivot,
basic automated postings and periodic transactions support,
more and easier addons,
bugfixes
Release contributors: Simon Michael, Mykola Orliuk, Justin Le, Peter Simons, Stefano Rodighiero, Moritz Kiefer, Pia Mancini, Bryan Richter, Steven R. Baker, Hans-Peter Deifel, Joshua Chia, Joshua Kehn, Michael Walker.
project-wide changes 1.2
Packaging
bump stack config to latest lts, bump brick to 0.15.2 to allow hledger-iadd install in hledger dir, update cabal files to latest hpack 0.17.0/stack 1.4 format (#512), use more accurate license tag in Cabal file (Peter Simons).
Finance
set up a hledger open collective (http://opencollective.com/hledger), more devguide links to issues with bounties, codefund link, start tracking and publishing project finances (dogfooding!).
Documentation and website
docs page & manual cleanups,
begin organising a cookbook,
update addons list,
move detailed addon docs out of hledger manual,
document addons installation,
explain print’s CSV output,
note an issue with balance assertions & multiple -f options,
clarify tags,
add github stars widget to home and devguide,
improve market price docs,
ui & web screenshots layout fixes,
fix extra whitespace after synopsis in hledger-web text manuals,
update accounts directive/budget/rewrite/read-related mockups,
drop old org notes.
Examples
consolidate extra/ and data/ in examples/, tarsnap csv rules & reporting example, xpensetracker csv rules.
Tools
Travis CI now checks functional tests/build warnings/addons, temporary workaround for Appveyor CI failures, remove accidentally committed pandoc executables, some pandoc filter fixes, mailmap file to clean up git log authors, bench.hs cleanup, fix gitignore of generated manuals, avoid excessive rebuilding with make [func]test, run functional tests more verbosely, add alex/happy update step to cabal-install.sh.
hledger-lib 1.2
journal format
A pipe character can optionally be used to delimit payee names in
transaction descriptions, for more accurate querying and pivoting by
payee. Eg, for a description like payee name | additional notes,
the two parts will be accessible as pseudo-fields/tags named payee
and note.
Some journal parse errors now show the range of lines involved, not just the first.
ledger format
The experimental ledger: reader based on the WIP ledger4 project has
been disabled, reducing build dependencies.
Misc
Fix a bug when tying the knot between postings and their parent transaction, reducing memory usage by about 10% (#483) (Mykola Orliuk)
Fix a few spaceleaks (#413) (Moritz Kiefer)
Add Ledger.Parse.Text to package.yaml, fixing a potential build failure.
Allow megaparsec 5.2 (#503)
Rename optserror -> usageError, consolidate with other error functions
hledger cli 1.2
CLI
“hledger” and “hledger -h” now print a better organised commands list and general usage message respectively (#297).
The common reporting flags can now be used anywhere on the command line.
Fixed deduplication of addons in commands list.
Fixed ugly stack traces in command line parse error messages.
The -V/--value flag is now a global report flag, so it works with
balance, print, register, balancesheet, incomestatement, cashflow,
etc. (Justin Le)
The --pivot global reporting option replaces all account names with
the value of some other field or tag. It has been improved, eg:
- we don’t add the field/tag name name as a prefix
- when pivoting on a tag, if the tag is missing we show a blank (rather than showing mixed tag values and account names)
- a pipe character delimiter may be used in descriptions to get a more accurate
and useful payee report (
hledger balance --pivot payee)
options cleanups
Addons
Easier installation: move add-ons and example scripts to bin/, convert to stack scripts, add a build script to install all deps, add some functional tests, test add-ons with Travis CI, add installation docs to download page.
Improved docs:
all addons now contain their own documentation. Most of them (all but
hledger-budget) use a new reduced-boilerplate declaration format
and can show short (-h) and long (--help) command line help.
(Long help is declared with pre and postambles to the generated
options help, short help is that truncated at the start of the hledger
common flags.)
hledger now shows a cleaner list of addon commands, showing only the
compiled version of an addon when both source and compiled versions
are in $PATH. (Addons with .exe extension or no extension are
considered compiled. Modification time is not checked, ie, an old
compiled addon will override a newer source version. If there are
three or more versions of an addon, all are shown. )
New addons added/included:
- autosync - example symlink to ledger-autosync
- budget - experimental budget reporting command supporting Ledger-like periodic transactions and automated transactions (Mykola Orliuk)
- chart - pie-chart-generating prototype, a repackaging of the old hledger-chart tool
- check - more powerful balance assertions (Michael Walker)
- check-dupes - find accounts sharing the same leaf name (Stefano Rodighiero)
- prices - show all market price records (Mykola Orliuk)
- register-match - a helper for ledger-autosync’s deduplication, finds best match for a transaction description
The equity command now always generates a valid journal transaction, handles prices better, and adds balance assertions (Mykola Orliuk).
The rewrite command is more robust and powerful (Mykola Orliuk):
-
in addition to command-line rewrite options, it understands rewrite rules defined in the journal, similar to Ledger’s automated transactions (#99). Eg:
= ^income (liabilities:tax) *.33 = expenses:gifts budget:gifts *-1 assets:budget *1 -
it can generate diff output, allowing easier review of the proposed changes, and safe modification of original journal files (preserving file-level comments and directives). Eg:
hledger-rewrite --diff Agency --add-posting 'Expenses:Taxes *0.17' | patch -
rewrites can affect multiple postings in a transaction, not just one.
-
posting-specific dates are handled better
balance
A new --pretty-tables option uses unicode characters for rendering
table borders in multicolumn reports (#522) (Moritz Kiefer)
balancesheet/cashflow/incomestatement
These commands are now more powerful, able to show multicolumn reports and generally having the same features as the balance command. (Justin Le)
balancesheet has always ignored a begin date specified with a -b or
-p option; now it also ignores a begin date specified with a date:
query. (Related discussion at #531)
The output of print is now always a valid journal (fixes #465) (Mykola Orliuk).
print now tries to preserves the format of implicit/explicit balancing
amounts and prices, by default. To print with all amounts explicit,
use the new --explicit/-x flag (fixes #442). (Mykola Orliuk)
Don’t lose the commodity of zero amounts/zero balance assertions (fixes #475) (Mykola Orliuk)
Misc
Fix a regression in the readability of option parsing errors (#478) (Hans-Peter Deifel)
Fix an example in Cli/Main.hs (Steven R. Baker)
Allow megaparsec 5.2 (#503)
hledger-ui 1.2
Fix a pattern match failure when pressing E on the transaction screen (fixes #508)
Accounts with ? in name had empty registers (fixes #498) (Bryan Richter)
Allow brick 0.16 (Joshua Chia) and brick 0.17/vty 0.15 (Peter Simons)
Allow megaparsec 5.2 (fixes #503)
Allow text-zipper 0.10
hledger-web 1.2
Accounts with ? in name had empty registers (fixes #498) (Bryan Richter)
Allow megaparsec 5.2 (fixes #503)
2016/12/31 hledger 1.1
more robust file format detection,
integration of WIP ledger4 parser,
balance assignments,
hledger-ui --watch,
hledger-iadd integration,
bugfixes
Release contributors: Simon Michael, Johannes Gerer, Mykola Orliuk, Shubham Lagwankar.
project-wide changes 1.1
misc
-
don’t show stack trace details in errors
-
more predictable file format detection
When we don’t recognise a file’s extension, instead of choosing a subset of readers to try based on content sniffing, now we just try them all. Also, this can be overridden by prepending the reader name and a colon to the file path (eg timedot:file.dat, csv:-).
-
avoid creating junk CSV rules files when trying alternate readers. We now create it only after successfully reading a file as CSV.
-
improvements to -B and -V docs: clearer descriptions, more linkage (#403)
hledger-lib 1.1
journal format
-
balance assignments are now supported (#438, #129, #157, #288)
This feature also brings a slight performance drop (~5%); optimisations welcome.
-
also recognise
*.hledgerfiles as hledger journal format
ledger format
-
use ledger-parse from the ledger4 project as an alternate reader for C++ Ledger journals
The idea is that some day we might get better compatibility with Ledger files this way. Right now this reader is not very useful and will be used only if you explicitly select it with a
ledger:prefix. It parses transaction dates, descriptions, accounts and amounts, and ignores everything else. Amount parsing is delegated to hledger’s journal parser, and malformed amounts might be silently ignored.This adds at least some of the following as new dependencies for hledger-lib: parsers, parsec, attoparsec, trifecta.
misc
-
update base lower bound to enforce GHC 7.10+
hledger-lib had a valid install plan with GHC 7.8, but currently requires GHC 7.10 to compile. Now we require base 4.8+ everywhere to ensure the right GHC version at the start.
-
Hledger.Read api cleanups
-
rename dbgIO to dbg0IO, consistent with dbg0, and document a bug in dbg*IO
-
make readJournalFiles [f] equivalent to readJournalFile f (#437)
-
more general parser types enabling reuse outside of IO (#439)
hledger cli 1.1
balance
-
with
-V, don’t ignore market prices in the future (#453, #403) -
with
-Vand multiple same-date market prices, use the last parsed not the highest price (#403)
misc
-
fix non-existent “oldtime” dependency (#431)
-
hledger-equity.hs now generates valid journal format when there are multiple commodities
hledger-ui 1.1
-
with
--watch, the display updates automatically to show file or date changeshledger-ui --watchwill reload data when the journal file (or any included file) changes. Also, when viewing a current standard period (ie this day/week/month/quarter/year), the period will move as needed to track the current system date. -
the
--changeflag shows period changes at startup instead of historical ending balances -
the A key runs the
hledger-iaddtool, if installed -
always reload when
gis pressedPreviously it would check the modification time and reload only if it looked newer than the last reload.
-
mark hledger-ui as “stable”
-
allow brick 0.15, vty 5.14, text-zipper 0.9
hledger-web 1.1
-
This came up in the context of Docker, but it seems it wasn’t possible for hledger-web to serve remote clients directly (without a proxy) because of 127.0.0.1 being hardcoded. That can now be changed with
--host=IPADDR. Also, the default base url uses this address rather than a hard-coded “localhost”. -
rename
--serverto--serveThe
--serverflag sounded too close in meaning to--hostso I’ve renamed it to--serve. The old spelling is still accepted, but deprecated and will be removed in the next release.
hledger-api 1.1
-
serves on 127.0.0.1 by default,
--hostoption added (#432)Consistent with hledger-web: serves only local requests by default, use
--host=IPADDRto change this. -
fixed the version string in command-line help and swagger info
2016/10/26 hledger 1.0
More hledger-ui features,
better hledger-web layout,
new hledger-api server,
new timedot format,
--pivot & --anon,
reorganized multi-format docs,
built-in help.
(mail)
Release contributors: Simon Michael, Dominik Süß, Thomas R. Koll, Moritz Kiefer, jungle-boogie, Sergei Trofimovich, Malte Brandy, Sam Doshi, Mitchell Rosen, Hans-Peter Deifel, Brian Scott, and Andrew Jones.
misc
-
added GHC 8 support, dropped GHC 7.6 and 7.8 support.
GHC 7.8 support could be restored with small code changes and a maintainer.
-
a cabal.project file has been added (Moritz Kiefer)
-
use hpack for maintaining cabal files (#371).
Instead of editing cabal files directly, we now edit the less verbose and less redundant package.yaml files and let stack (or hpack) update the cabal files. We commit both the .yaml and .cabal files.
-
clean up some old cabal flags
-
tools/simplebench has been spun off as the quickbench package.
-
add Appveyor CI builds, provide more up-to-date Windows binaries
-
extra: add a bunch of CSV rules examples
docs
-
the website is simpler, clearer, and more mobile-friendly.
Docs are now collected on a single page and organised by type: getting started, reference, more.
-
reference docs have been split into one manual for each executable and file format.
This helps with maintenance and packaging and also should make it easier to see what’s available and to read just what you need.
-
manuals are now provided in html, plain text, man and info formats
generated from the same source by a new Shake-based docs build system. (#292)
-
versioned manuals are provided on the website, covering recent releases and the latest dev version (#385, #387)
-
manuals are built in to the hledger executables, allowing easy offline reading on all platforms.
PROG -h shows PROG's command-line usage PROG --help shows PROG's manual (fixed width) PROG --man shows PROG's manual with man (formatted/paged) PROG --info shows PROG's manual with info (hypertext) hledger help [TOPIC] shows any manual hledger man [TOPIC] shows any manual with man hledger info [TOPIC] shows any manual with info -
the general and reporting options are now listed in all executable manuals.
We assume any of them which are unsupported are harmlessly ignored.
-
demo.hledger.org is using beancount’s example journal.
This is the somewhat realistic example journal from the beancount project, tweaked for hledger.
-
minor copyedits (jungle-boogie)
cli
-
parsing multiple input files is now robust.
When multiple
-foptions are provided, we now parse each file individually rather than just concatenating them, so they can have different formats (#320). Note this also means that directives (likeYoralias) no longer carry over from one file to the next. -
-Ihas been added as the short flag for--ignore-assertions(this is different from Ledger’s CLI, but useful for hledger-ui).
-
parsing an argument-less
--debugoption is more robust
hledger-lib 1.0
timedot format
-
new “timedot” format for retroactive/approximate time logging.
Timedot is a plain text format for logging dated, categorised quantities (eg time), supported by hledger. It is convenient for approximate and retroactive time logging, eg when the real-time clock-in/out required with a timeclock file is too precise or too interruptive. It can be formatted like a bar chart, making clear at a glance where time was spent.
timeclock format
-
renamed “timelog” format to “timeclock”, matching the emacs package
-
sessions can no longer span file boundaries (unclocked-out
sessions will be auto-closed at the end of the file).
-
transaction ids now count up rather than down (#394)
-
timeclock files no longer support default year directives
-
removed old code for appending timeclock transactions to journal transactions.
A holdover from the days when both were allowed in one file.
csv format
- fix empty field assignment parsing, rule parse errors after megaparsec port (#407) (Hans-Peter Deifel)
journal format
-
journal files can now include timeclock or timedot files (#320)
(but not yet CSV files).
-
fixed an issue with ordering of same-date transactions included from other files
-
the “commodity” directive and “format” subdirective are now supported, allowing
full control of commodity style (#295) The commodity directive’s format subdirective can now be used to override the inferred style for a commodity, eg to increase or decrease the precision. This is at least a good workaround for #295.
-
Ledger-style “apply account”/“end apply account” directives are now used to set a default parent account.
-
the Ledger-style “account” directive is now accepted (and ignored).
-
bracketed posting dates are more robust (#304)
Bracketed posting dates were fragile; they worked only if you wrote full 10-character dates. Also some semantics were a bit unclear. Now they should be robust, and have been documented more clearly. This is a legacy undocumented Ledger syntax, but it improves compatibility and might be preferable to the more verbose “date:” tags if you write posting dates often (as I do). Internally, bracketed posting dates are no longer considered to be tags. Journal comment, tag, and posting date parsers have been reworked, all with doctests.
-
balance assertion failure messages are clearer
-
with
--debug=2, more detail about balance assertions is shown.
misc
-
file parsers have been ported from Parsec to Megaparsec \o/ (#289, #366) (Alexey Shmalko, Moritz Kiefer)
-
most hledger types have been converted from String to Text, reducing memory usage by 30%+ on large files
-
file parsers have been simplified for easier troubleshooting (#275).
The journal/timeclock/timedot parsers, instead of constructing opaque journal update functions which are later applied to build the journal, now construct the journal directly by modifying the parser state. This is easier to understand and debug. It also rules out the possibility of journal updates being a space leak. (They weren’t, in fact this change increased memory usage slightly, but that has been addressed in other ways). The ParsedJournal type alias has been added to distinguish “being-parsed” journals and “finalised” journals.
-
file format detection is more robust.
The Journal, Timelog and Timedot readers’ detectors now check each line in the sample data, not just the first one. I think the sample data is only about 30 chars right now, but even so this fixed a format detection issue I was seeing. Also, we now always try parsing stdin as journal format (not just sometimes).
-
all file formats now produce transaction ids, not just journal (#394)
-
git clone of the hledger repo on windows now works (#345)
-
added missing benchmark file (#342)
-
our stack.yaml files are more compatible across stack versions (#300)
-
use newer file-embed to fix ghci working directory dependence
-
report more accurate dates in account transaction report when postings have their own dates
(affects hledger-ui and hledger-web registers). The newly-named “transaction register date” is the date to be displayed for that transaction in a transaction register, for some current account and filter query. It is either the transaction date from the journal (“transaction general date”), or if postings to the current account and matched by the register’s filter query have their own dates, the earliest of those posting dates.
-
simplify account transactions report’s running total.
The account transactions report used for hledger-ui and -web registers now gives either the “period total” or “historical total”, depending strictly on the
--historicalflag. It doesn’t try to indicate whether the historical total is the accurate historical balance (which depends on the user’s report query). -
reloading a file now preserves the effect of options, query arguments etc.
-
reloading a journal should now reload all included files as well.
-
the Hledger.Read.* modules have been reorganised for better reuse.
Hledger.Read.Utils has been renamed Hledger.Read.Common and holds low-level parsers & utilities; high-level read utilities are now in Hledger.Read.
-
clarify amount display style canonicalisation code and terminology a bit.
Individual amounts still have styles; from these we derive the standard “commodity styles”. In user docs, we might call these “commodity formats” since they can be controlled by the “format” subdirective in journal files.
-
Journal is now a monoid
-
expandPath now throws a proper IO error
-
more unit tests, start using doctest
hledger cli 1.0
add
-
suggest only one commodity at a time as default amount (#383)
(since we currently can’t input more than one at a time)
balance
-
added
--changeflag for consistency -
-H/--historicalnow also affects single-column balance reports with a start date (#392).This has the same effect as just omitting the start date, but adds consistency.
-
in CSV output, render amounts in one-line format (#336)
balancesheet
- fix an infinite loop (#393)
- in CSV output, fix and rename the transaction id field
register
-
fix a sorting regression with
--date2(#326) -
--average/-Ais now affected by--historical/-H -
added
--cumulativeflag for consistency -
in CSV output, include the transaction id and rename the total field (#391)
stats
- fixed an issue with ordering of include files
misc
-
--pivotoption added, groups postings by tag instead of account (#323) (Malte Brandy) -
--anonoption added, obfuscates account names and descriptions (#265) (Brian Scott)(Only affects the hledger tool, for now.)
-
try to clarify balance/register’s various report modes,
kinds of “balance” displayed, and related options and language.
-
with multiple
--change/--cumulative/--historicalflags, use the last one instead of complaining -
don’t add the “d” suffix when displaying day periods
-
stack-ify extra/hledger-rewrite.hs
hledger-ui 1.0
accounts screen
-
at depth 0, show accounts on one “All” line and show all transactions in the register
-
0 now sets depth limit to 0 instead of clearing it
-
always use
--no-elidefor a more regular accounts tree
register screen
-
registers can now include/exclude subaccount transactions.
The register screen now includes subaccounts’ transactions if the accounts screen was in tree mode, or when showing an account which was at the depth limit. Ie, it always shows the transactions contributing to the balance displayed on the accounts screen. As on the accounts screen, F toggles between tree mode/subaccount txns included by default and flat mode/subaccount txns excluded by default. (At least, it does when it would make a difference.)
-
register transactions are filtered by realness and status (#354).
Two fixes for the account transactions report when
--real/--cleared/real:/status:are in effect, affecting hledger-ui and hledger-web:-
exclude transactions which affect the current account via an excluded posting type. Eg when
--realis in effect, a transaction posting to the current account with only virtual postings will not appear in the report. -
when showing historical balances, don’t count excluded posting types in the starting balance. Eg with
--real, the starting balance will be the sum of only the non-virtual prior postings.This is complicated and there might be some ways to confuse it still, causing wrongly included/excluded transactions or wrong historical balances/running totals (transactions with both real and virtual postings to the current account, perhaps ?)
-
-
show more accurate dates when postings have their own dates.
If postings to the register account matched by the register’s filter query have their own dates, we show the earliest of these as the transaction date.
misc
-
H toggles between showing “historical” or “period” balances (#392).
By default hledger-ui now shows historical balances, which include transactions before the report start date (like hledger balance
--historical). Use the H key to toggle to “period” mode, where balances start from 0 on the report start date. -
shift arrow keys allow quick period browsing
- shift-down narrows to the next smaller standard period (year/quarter/month/week/day), shift-up does the reverse
- when narrowed to a standard period, shift-right/left moves to the next/previous period
- `t` sets the period to today.
-
a runs the add command
-
E runs $HLEDGERUIEDITOR or $EDITOR or a default editor (vi) on the journal file.
When using emacs or vi, if a transaction is selected the cursor will be positioned at its journal entry.
-
/ key sets the filter query; BACKSPACE/DELETE clears it
-
Z toggles display of zero items (like
--empty), and they are shown by default.-E/--emptyis now the default for hledger-ui, so accounts with 0 balance and transactions posting 0 change are shown by default. The Z key toggles this, entering “nonzero” mode which hides zero items. -
R toggles inclusion of only real (non-virtual) postings
-
U toggles inclusion of only uncleared transactions/postings
-
I toggles balance assertions checking, useful for troubleshooting
-
vi-style movement keys are now supported (for help, you must now use ? not h) (#357)
-
ESC cancels minibuffer/help or clears the filter query and jumps to top screen
-
ENTER has been reserved for later use
-
reloading now preserves any options and modes in effect
-
reloading on the error screen now updates the message rather than entering a new error screen
-
the help dialog is more detailed, includes the hledger-ui manual, and uses the full terminal width if needed
-
the header/footer content is more efficient; historical/period and tree/flat modes are now indicated in the footer
-
date: query args on the command line now affect the report period.
A
date2: argor--date2flag might also affect it (untested). -
hledger-ui now uses the quicker-building microlens
hledger-web 1.0
ui
-
use full width on large screens, hide sidebar on small screens, more standard bootstrap styling (#418, #422) (Dominik Süß)
-
show the sidebar by default (#310)
-
fix the add link’s tooltip
-
when the add form opens, focus the first field (#338)
-
leave the add form’s date field blank, avoiding a problem with tab clearing it (#322)
-
use transaction id instead of date in transaction urls (#308) (Thomas R. Koll)
-
after following a link to a transaction, highlight it (Thomas R. Koll)
-
misc. HTML/CSS/file cleanups/fixes (Thomas R. Koll)
misc
-
startup is more robust (#226).
Now we exit if something is already using the specified port, and we don’t open a browser page before the app is ready.
-
termination is more robust, avoiding stray background threads.
We terminate the server thread more carefully on exit, eg on control-C in GHCI.
-
more robust register dates and filtering in some situations (see hledger-ui notes)
-
reloading the journal preserves options, arguments in effect (#314).
The initial query specified by command line arguments is now preserved when the journal is reloaded. This does not appear in the web UI, it’s like an invisible extra filter.
-
show a proper not found page on 404
-
document the special `inacct:` query (#390)
hledger-api 1.0
misc
-
new hledger-api tool: a simple web API server with example clients (#316)
-
start an Angular-based API example client (#316) (Thomas R. Koll)
2008-2015 Pre-1.0
2015/10/30 hledger 0.27
New curses-style interface, market value reporting, wide characters, fast regex aliases, man pages (mail)
Release contributors: Simon Michael, Carlos Lopez-Camey.
hledger 0.27:
Account aliases:
- Regular expression account aliases are now fast enough that you can use lots of them without slowing things down. They now take O(aliases x accounts) time, instead of O(aliases x transactions); also, regular expressions are no longer recompiled unnecessarily.
Documentation:
- The hledger packages now have man pages, based on the current user manual, thanks to the mighty pandoc (#282).
Journal format:
-
Dates must now begin with a digit (not /, eg).
-
The comment directive longer requires an end comment, and will extend to the end of the file(s) without it.
Command-line interface:
-
Output (balance reports, register reports, print output etc.) containing wide characters, eg chinese/japanese/korean characters, should now align correctly, when viewed in apps and fonts that show wide characters as double width (#242).
-
The argument for
--depthordepth:must now be positive.
add:
-
Journal entries are now saved with all amounts explicit, to avoid losing price info (#283).
-
Fixed a bug which sometimes (when the same letter pair was repeated) caused it not to pick the most similar past transaction for defaults.
balance:
-
There is now a
-V/--valueflag to report current market value (as in Ledger). It converts all reported amounts using their “default market price”. “Market price” is the new name for “historical prices”, defined with the P directive. The default market price for a commodity is the most recent one found in the journal on or before the report end date.Unlike Ledger, hledger’s
-Vuses only the market prices recorded with P directives; it does not use the transaction prices recorded as part of posting amounts. Using both-Band-Vat the same time is possible. -
Fixed a bug in amount normalization which caused amount styles (commodity symbol placement, decimal point character, etc.) to be lost in certain cases (#230, #276).
-
The balance command’s
--formatoption can now adjust the rendering style of multi-commodity amounts, if you begin the format string with one of:%_ - renders amounts on multiple lines, bottom-aligned (the default) %^ - renders amounts on multiple lines, top-aligned %, - renders amounts on one line, comma-separated
-
The balance report’s final total (and the line above it) now adapt themselves to a custom
--format.
print:
-
The
--matchoption prints the journal entry that best matches a description (ie whose description field is most similar to the value given, and if there are several equally similar, the most recent). This was originally an add-on I used to guess account names for ledger-autosync. It’s nice for quickly looking up a recent transaction from a guessed or partial description. -
print now always right-aligns the amounts in an entry, even when they are wider than 12 characters. (If there is a price, it’s considered part of the amount for right-alignment.)
register:
- Amount columns now resize automatically, using more space if it’s needed and available.
hledger-ui 0.27:
-
hledger-ui is a new curses-style UI, intended to be a standard part of the hledger toolset for all users (except on native MS Windows, where the vty lib is not yet supported).
The UI is quite simple, allowing just browsing of accounts and transactions, but it has a number of improvements over the old hledger-vty, which it replaces:
- adapts to screen size
- handles wide characters
- shows multi-commodity amounts on one line
- manages cursor and scroll position better
- allows depth adjustment
- allows
--flattoggle - allows
--clearedtoggle - allows journal reloading
- shows a more useful transaction register, like hledger-web
- offers multiple color themes
- includes some built-in help
hledger-ui is built with brick, a new higher-level UI library based on vty, making it relatively easy to grow and maintain.
hledger-web 0.27:
-
Fix keyboard shortcut for adding a transaction (Carlos Lopez-Camey)
-
Clear the form when clicking ‘Add a transaction’ (just like the shortcut) (Carlos Lopez-Camey)
-
Disallow -f- (reading from standard input) which currently doesn’t work (#202)
-
Fix broken links when using
--base-url(#235) -
Fix the
--file-urloption (#285) -
Show fewer “other accounts” in the account register: to reduce clutter in the “other accounts” field, if there are both real and virtual postings to other accounts, show only the accounts posted to by real postings.
2015/7/12 hledger 0.26
Website & doc updates, account aliases, misc. bugfixes & cleanups, performance.
Release contributors: Simon Michael, Imuli, Carlos Lopez-Camey, Kyle Marek-Spartz, Rick Lupton, Simon Hengel.
Changes to hledger.org & docs:
- examples everywhere, screenshots, content & style updates
- manual: reorganise topics, add some undocumented things, clarify some things
- dev guide: more links, put how-tos first, copy diagram from old wiki, update the setup docs
User-visible changes in hledger since 0.25.1:
Account aliases:
-
Account aliases are once again non-regular-expression-based, by default. (#252)
The regex account aliases added in 0.24 tend to trip up people switching between hledger and Ledger. (Also they are currently slow). We now use the old non-regular-expression aliases again, by default; these are unsurprising, useful, and pretty close in functionality to Ledger’s aliases.
The new regex aliases are still available, but they must now be enclosed in forward slashes. (Ledger effectively ignores these.)
Journal format:
-
We now parse, and also print, journal entries with no postings, as proposed on the mail lists. These are not well-formed General Journal entries/transactions, but on the other hand: Ledger and beancount parse them; if they are parsed, they should be printed; they provide a convenient way to record (and report) non-transaction events; and they permit more gradual introduction and learning of the concepts (so eg a beginner can keep a simple journal before learning about accounts and postings).
-
Trailing whitespace after a
commentdirective is now ignored.
Command-line interface:
- The
-f/file option may now be used multiple times. This is equivalent to concatenating the input files before running hledger. The add command adds entries to the first file specified.
Queries:
-
real: (no argument) is now a synonym for real:1
-
tag: now matches tag names with a regular expression, like most other queries
-
empty: is no longer supported, as it overlaps a bit confusingly with amt:0. The
--emptyflag is still available. -
You can now match on pending status (#250)
A transaction/posting status of ! (pending) was effectively equivalent to * (cleared). Now it’s a separate state, not matched by
--cleared. The new Ledger-compatible--pendingflag matches it, and so does--uncleared.The relevant search query terms are now status:*, status:! and status: (the old status:1 and status:0 spellings are deprecated).
Since we interpret
--unclearedand status: as “any state except cleared”, it’s not currently possible to match things which are neither cleared nor pending.
activity:
- activity no longer excludes 0-amount postings by default.
add:
- Don’t show quotes around the journal file path in the “Creating…” message, for consistency with the subsequent “Adding…” message.
balancesheet:
- Accounts beginning with “debt” or now also recognised as liabilities.
print:
-
We now limit the display precision of inferred prices. (#262)
When a transaction posts to two commodities without specifying the conversion price, we generate a price which makes it balance (cf <http://hledger.org/journal.html#transaction-prices). The print command showed this with full precision (so that manual calculations with the displayed numbers would look right), but this sometimes meant we showed 255 digits (when there are multiple postings in the commodity being priced, and the averaged unit price is an irrational number). In this case we now set the price’s display precision to the sum of the (max) display precisions of the commodities involved. An example:
hledger -f- print <<< 1/1 c C 10.00 c C 11.00 d D -320.00 >>> 2015/01/01 c C 10.00 @ D 15.2381 c C 11.00 @ D 15.2381 d D -320.00 >>>=0There might still be cases where this will show more price decimal places than necessary.
-
We now show inferred unit prices with at least 2 decimal places.
When inferring prices, if the commodities involved have low display precisions, we don’t do a good job of rendering accurate-looking unit prices. Eg if the journal doesn’t use any decimal places, any inferred unit prices are also displayed with no decimal places, which makes them look wrong to the user. Now, we always give inferred unit prices a minimum display precision of 2, which helps a bit.
register:
- Postings with no amounts could give a runtime error in some obscure case, now fixed.
stats:
- stats now supports
-o/--outputfile, like register/balance/print. - An O(n^2) performance slowdown has been fixed, it’s now much faster on large journals.
+--------------------------------------++--------+--------+ | || 0.25 | 0.26 | +======================================++========+========+ | -f data/100x100x10.journal stats || 0.10 | 0.16 | | -f data/1000x1000x10.journal stats || 0.45 | 0.21 | | -f data/10000x1000x10.journal stats || 58.92 | 2.16 | +--------------------------------------++--------+--------+
Miscellaneous:
- The June 30 day span was not being rendered correctly; fixed. (#272)
- The deprecated shakespeare-text dependency has been removed more thoroughly.
- The bench script invoked by “cabal bench” or “stack bench” now runs
some simple benchmarks.
You can get more accurate benchmark times by running with
--criterion. This will usually give much the same numbers and takes much longer. Or with--simplebench, it benchmarks whatever commands are configured in bench/default.bench. This mode uses the first “hledger” executable in $PATH.
User-visible changes in hledger-web since 0.25.1:
- make the j keybinding respect
--base-url(fixes #271) - respect command line options (fixes #225)
- include the unminified jquery source again (#161)
- fix build breakage from #165 (fixes #268)
- fix a js error breaking add form in browsers other than firefox (fixes #251)
- drop deprecated network-conduit dependency
2015/4/29 hledger-web 0.25.1
- support/require base-compat >0.8 (#245)
2015/4/29 hledger 0.25.1
- timelog: support the description field (#247)
2015/4/29 hledger-lib 0.25.1
- support/require base-compat >0.8 (#245)
2015/4/7 hledger 0.25
GHC 7.10 compatibility, terminal width awareness, useful averages and totals columns, and a more robust hledger-web add form.
Release contributors: Simon Michael, Julien Moutinho.
User-visible changes in hledger since 0.24.1:
-
GHC 7.10 compatibility (#239)
-
On POSIX systems, the register command now uses the full terminal width by default. Specifically, the output width is set from:
- a
--widthoption - or a COLUMNS environment variable (NB: not the same as a bash shell var)
- or on POSIX (non-windows) systems, the current terminal width
- or the default, 80 characters.
This feature requires the C curses dev libraries, making installation slightly harder. If that’s a problem you can disable curses support with a cabal flag:
cabal install -f-curses .... - a
-
register’s
--widthoption now accepts an optional description column width following the overall width (--width WIDTH[,DESCWIDTH]). This also sets the account column width, since the available space (WIDTH-41) is divided up between these two columns. Here’s a diagram:
<--------------------------------- width (W) ---------------------------------->
date (10) description (D) account (W-41-D) amount (12) balance (12)
DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaa AAAAAAAAAAAA AAAAAAAAAAAA
Examples:
$ hledger reg # use terminal width on posix
$ hledger reg -w 100 # width 100, equal description/account widths
$ hledger reg -w 100,40 # width 100, wider description
$ hledger reg -w $COLUMNS,100 # terminal width and set description width
-
balance: new
-T/--row-totaland-A/--averageoptionsIn multicolumn balance reports,
-T/--row-totalnow shows a totals column and-A/--averageshows an averages column. This helps eg to see monthly average expenses (hledger bal ^expenses -MA).NB our use of
-Tdeviates from Ledger’s UI, where-Tsets a custom final total expression. -
balance:
-Nis now short for--no-total -
balance: fix partially-visible totals row with
--no-totalA periodic (not using
--cumulativeor--historical) balance report with--no-totalnow hides the totals row properly. -
journal, csv: comment lines can also start with *
As in Ledger. This means you can embed emacs org/outline-mode nodes in your journal file and manipulate it like an outline.
User-visible changes in hledger-web since 0.24.1:
-
GHC 7.10 compatibility (#239)
-
fix the add form when there are included files (#234)
NB to make this work, the add form now shows the full file path of the main and included journal files.
-
improve add form validation (#223, #234)
All add form errors are displayed as form errors, not internal server errors, and when there are errors the add form is redisplayed (form inputs are not preserved, currently).
-
keep the add button right-aligned when pressing ctrl - on the add form
2015/3/15 hledger 0.24.1
- timelog: show hours with 2 decimal places, not 1 (#237)
- fix balance accumulation through assertions in several commodities (#195)
- fix rendering of week 52 heading in weekly reports
- allow utf8-string-1 (fpco/stackage/#426)
2015/3/15 hledger-lib 0.24.1
- fix JournalReader “ctx” compilation warning
- add some type signatures in Utils to help make ghci-web
2015/1/10 hledger-web 0.24.1
- add missing modules to fix cabal tests (#232)
2014/12/25 hledger 0.24
Release contributors: Simon Michael, Julien Moutinho, Ryan Desfosses, Gergely Risko, Gwern Branwen.
CSV export, a non-floating point number representation, more powerful account aliases, speedups, and a streamlined web UI.
User-visible changes in hledger since 0.23.3:
General:
- fix redundant compilation when cabal installing the hledger packages
- switch to Decimal for representing amounts (#118)
- report interval headings (eg in balance, register reports) are shown compactly when possible
- general speedups.
+--------------------------------------------++----------------+--------------+--------+
| || hledger-0.23.3 | hledger-0.24 | ledger |
+============================================++================+==============+========+
| -f data/100x100x10.journal balance || 0.05 | 0.03 | 0.01 |
| -f data/1000x1000x10.journal balance || 0.34 | 0.21 | 0.04 |
| -f data/10000x1000x10.journal balance || 2.72 | 1.48 | 0.19 |
| -f data/10000x1000x10.journal balance aa || 3.16 | 1.55 | 0.14 |
| -f data/100x100x10.journal register || 0.09 | 0.05 | 0.04 |
| -f data/1000x1000x10.journal register || 0.66 | 0.32 | 0.30 |
| -f data/10000x1000x10.journal register || 6.27 | 2.77 | 2.80 |
| -f data/10000x1000x10.journal register aa || 3.30 | 1.62 | 0.21 |
| -f data/100x100x10.journal print || 0.06 | 0.05 | 0.01 |
| -f data/1000x1000x10.journal print || 0.42 | 0.25 | 0.04 |
| -f data/10000x1000x10.journal print || 3.95 | 2.57 | 0.38 |
| -f data/10000x1000x10.journal print aa || 3.23 | 1.56 | 0.14 |
| -f data/100x100x10.journal stat || 0.04 | 0.03 | 0.01 |
| -f data/1000x1000x10.journal stat || 0.35 | 0.24 | 0.03 |
| -f data/10000x1000x10.journal stat || 14.84 | 13.29 | 0.20 |
| -f data/10000x1000x10.journal stat aa || 12.08 | 10.16 | 0.17 |
+--------------------------------------------++----------------+--------------+--------+
Journal format:
- detect decimal point and digit groups more robustly (#196)
- check that transaction dates are followed by whitespace or newline
- check that dates use a consistent separator character
- balance assertions now are specific to a single commodity, like Ledger (#195)
- support multi-line comments using “comment”, “end comment” directives, like Ledger
CSV format:
- fix: reading CSV data from stdin now works better
- the original order of same-day transactions is now usually preserved (if the records appear to be in reverse date order, we reverse them before finally sorting by transaction date)
- the rules file include directive is now relative to the current file’s directory (#198)
- CSV output is now built in to the balance, print, and register
commands, controlled by
-O/--output-format(and-o/--output-file, see below). This means that hledger data can be easily exported, eg for spreadsheet reporting or to migrate to a different tool.
CLI:
- the
--widthand--debugoptions now require their argument (#149) - when an option is repeated, the last value takes precedence (#219). This is helpful eg for customising your reporting command aliases on the fly.
- smart dates (used in
-p/-b/-e/date:/date2:) now must use a consistent separator character, and must be parseable to the end - output destination and format selection is now built in to the
balance, print and register commands, controlled by
-o/--output-fileand-O/--output-formatoptions. Notes:-o -means stdout. An output file name suffix matching a supported format will also set the output format, unless overridden by--output-format. Commands’ supported output formats are listed in their command-line help. Two formats are currently available: txt (the default) and csv. - balance assertions can be disabled with
--ignore-assertions
Account aliases:
- all matching account aliases are now applied, not just one directive and one option
- account aliases now match by case insensitive regular expressions matching anywhere in the account name
- account aliases can replace multiple occurrences of the pattern within an account name
- an account alias replacement pattern can reference matched groups with \N
Queries:
- date:/date2: with a malformed date now reports an error instead of being ignored
- amt: now supports >= or <=
- clarify status: docs and behaviour; “*” is no longer a synonym for “1” (fixes #227)
balance:
- fix: in tree mode,
--dropis ignored instead of showing empty account names - a depth limit of 0 now shows summary items with account name “…”, instead of an empty report (#206)
- in multicolumn balance reports,
-Enow also shows posting-less accounts with a non-zero balance during the period (in addition to showing leading & trailing empty columns) - in multicolumn reports, multi-commodity amounts are rendered on one line for better layout (#186)
- multicolumn reports’ title now includes the report span
register:
- runs faster with large output
- supports date2:, and date:/date2: combined with
--date2, better (fixes #201, #221, #222) - a depth limit of 0 now shows summary items (see balance)
-A/--averagenow implies-E/--empty- postings with multi-commodity amounts are now top-aligned, like Ledger
User-visible changes in hledger-web since 0.23.3:
General:
- fix: add missing hs/js files to package
- the web UI has been streamlined, dropping the raw and entries views and the edit form
- the help dialog has been improved
- keyboard shortcuts are now available
- the sidebar can be toggled open or closed (press s)
Journal view:
- layout tweaks for less truncation of descriptions and account names
Register view:
- fix: don’t show all zero amounts when searching by account within an account register view
- chart improvements: show zero balances with correct commodity; show accurate balance at all dates; show transaction events & tooltips; show zero/today lines & background colors
Add form:
- parses data more strictly and gives better errors (eg #194)
- allows any number of postings, not just two
- after adding a transaction, goes back to the journal
- keyboard shortcut (a) allows quick access
Dependencies:
- allow warp 3*, wai-handler-launch 3*
- require yesod 1.4* (fixes #212)
- js updated (jquery, bootstrap, flot), added (typeahead, cookie, hotkeys), removed (select2)
API-ish changes in hledger-lib since 0.23.3:
- fix combineJournalUpdates folding order
- fix a regexReplaceCI bug
- fix a splitAtElement bug with adjacent separators
- mostly replace slow regexpr with regex-tdfa (fixes #189)
- use the modern Text.Parsec API
- allow transformers 0.4*
- regexReplace now supports backreferences
- Transactions now remember their parse location in the journal file
- export Regexp types, disambiguate CsvReader’s similarly-named type
- export failIfInvalidMonth/Day (closes #216)
- track the commodity of zero amounts when possible (useful eg for hledger-web’s multi-commodity charts)
- show posting dates in debug output
- more debug helpers
2014/9/12 hledger-web 0.23.3
- remove warp, wai-handler-launch upper bounds (fixes #205)
2014/9/12 hledger 0.23.3
- allow text 1.2+ (fixes #207)
2014/5/8 hledger 0.23.2
- register: also fix date sorting of postings (#184)
2014/5/7 hledger 0.23.1
- register: fix a refactoring-related regression that the tests missed: if transactions were not ordered by date in the journal, register could include postings before the report start date in the output. (#184)
- add: don’t apply a default commodity to amounts on entry (#138)
- cli: options before the add-on command name are now also passed to it (#182)
- csv: allow the first name in a fields list to be empty (#178)
- csv: don’t validate fields count in skipped lines (#177)
2014/5/1 hledger 0.23
command-line fixes and polish, a new accounts
command, and a number of changes to the balance command relating
to --depth, --flat, and multicolumn mode, which I find has made it much
more useful.
mail
Changes since 0.22.2:
Journal format:
- A # (hash) in column 0 is now also supported for starting a top-level journal comment, like Ledger.
- The “too many missing amounts” error now reminds about the 2-space rule.
- Fix: . (period) is no longer parsed as a valid amount.
- Fix: default commodity directives no longer limit the maximum display precision (#169).
- Fix: + before an amount is no longer parsed as part of the commodity (#181).
CLI:
- Command-line help cleanups, layout improvements.
- Descriptions are shown for known add-ons in the command list.
- Command aliases have been simplified.
- Add-ons can now have any of these file extensions: none, hs, lhs, pl, py, rb, rkt, sh, bat, com, exe.
- Add-ons are displayed without their file extensions when possible.
- Add-ons with the same name as a built-in command or alias are ignored.
- Fix: add-on detection and invocation now works on windows.
- Fix: add-ons with digits in the name are now found.
- Fix: add-on arguments containing a single quote now work.
- Fix: when – is used to hide add-on options from the main program, it is no longer passed through as an add-on argument.
accounts:
- An accounts command has been added, similar to Ledger’s, for listing account names in flat or hierarchical mode.
add:
- Tab completion now works at all prompts, and will insert the default if the input area is empty.
- Account and amount defaults are more robust and useful.
- Transactions may also be completed by the enter key, when there are no more default postings.
- Input prompts are displayed in a different colour when supported.
balance:
- Balance reports in flat mode now always show exclusive (subaccount-excluding) balances.
- Balance reports in flat mode with
--depthnow aggregate deeper accounts at the depth limit instead of excluding them. - Multicolumn reports in flat mode now support
--drop. - Multicolumn balance reports can now show the account hierarchy with
--tree. - Multicolumn report start/end dates are adjusted to encompass the displayed report periods, so the first and last periods are “full” and comparable to the others.
- Fix: zero-balance leaf accounts below a non-zero-balance parent are no longer always shown (#170).
- Fix: multicolumn reports now support
--date2(cf #174).
balancesheet, cashflow, incomestatement:
- These commands now support
--flatand--drop.
print:
- Tag queries (tag:) will now match a transaction if any of its postings match.
register:
- The
--displayoption has been dropped. To see an accurate running total which includes the prior starting balance, use--historical/-H(like balance). - With a report interval, report start/end dates are adjusted to encompass the displayed periods, so the first and last periods are “full” and comparable to the others.
- Fix:
--date2now works with report intervals (fixes #174).
Queries:
- The currency/commodity query prefix (sym:) has been renamed to cur:.
- Currency/commodity queries are applied more strongly in register and balance reports, filtering out unwanted currencies entirely. Eg hledger balance cur:‘$’ now reports only the dollar amounts even if there are multi-currency transactions or postings.
- Amount queries like
amt:N,amt:<Nandamt:>N, where N is not 0, now do an unsigned comparison of the amount and N. That is, they compare the absolute magnitude. To do a signed comparison instead, write N with its sign (eg amt:+N, amt:<+N, amt:>-N). - Fix: amount queries no longer give false positives on multi-commodity amounts.
Miscellaneous:
- Default report dates now derive from the secondary dates when
--date2is in effect. - Default report dates now notice any posting dates outside the transaction dates’ span.
- Debug output improvements.
- New add-on example: extra/hledger-rewrite.hs, adds postings to matched entries.
- Compatible with GHC 7.2 (#155) - GHC 7.8, shakespeare 2
2014/5/1 hledger-web 0.23
Changes since 0.22.8:
- The
--static-rootflag has been renamed to--file-url. - hledger-web now builds with Cabal’s default
-O, not-O2, so may be a little quicker/less memory-hungry to install.
2014/4/29 hledger-web 0.22.8
- allow shakespeare 2.* (#179)
2014/4/17 hledger-web 0.22.7
- add Peter Simons’ patch fixing Data.Conduit.Network HostIPv4 error (#171)
2014/4/16 hledger-web 0.22.6
- depend on hledger[-lib] 0.22.2
2014/4/16 hledger 0.22.2
- display years before 1000 with four digits, not three
- avoid pretty-show to build with GHC < 7.4
- allow text 1.1, drop data-pprint to build with GHC 7.8.x
2014/4/15 hledger-web 0.22.5
- allow http-client 0.3.*, fixing cabal install again with GHC <= 7.6 (not yet 7.8)
- use pretty-show only with GHC 7.4+, fixing GHC 7.2 (fixes #155)
- allow warp 2.1, fixing cabal install
2014/2/10 hledger-web 0.22.4
- web: include the right unminified version of jquery.url.js (1.1) to avoid js breakage
2014/2/10 hledger-web 0.22.3
- web: fix version number reported by
--version
2014/2/10 hledger-web 0.22.2
New:
- web: new option
--static-rootto set the base url for static files
Improved:
- web: include unminified source of all javascript to help packagers (fixes #161)
- web: work around clang-related build failures with OS X mavericks/XCode 5
- web: allow blaze-html 0.7 (closes #159)
2014/1/6 hledger 0.22.1
-
require the latest pretty-show so hledger installation no longer needs an upgraded version of happy, and the docs build on hackage
-
require regex-tdfa directly instead of regex-compat-tdfa, simplifying Debian packaging
2013/12/13 hledger 0.22
New:
-
balance: with a reporting interval (monthly, yearly etc.), the balance command will now show a multi-column report, showing either the per-period changes in balance (by default), the period ending balances starting from zero (
--cumulative), or the actual period ending balances (--historical). A more detailed specification of the balance command’s behaviour has been added to Hledger.Cli.Balance. -
csv: rules files can now include other rules files, useful for factoring out common rules
-
queries:
sym:REGEXPmatches commodity symbols -
register:
--average/-Ashows a running average, like ledger -
in period expressions,
-(hyphen) can be used as a more compact synonym forfromandto. Eg:-p 2012/12/1-2013/2/1ordate:aug-. -
the add-on script examples in extra/ have been updated; get the hledger source and add …/hledger/extra/ to your PATH to make them available. They include:
hledger-accountnames.hs- print account nameshledger-balance-csv.hs- print a balance report as CSVhledger-equity.hs- print an entry matching all account balances (like ledger)hledger-print-unique.hs- print only journal entries unique descriptionshledger-register-csv.hs- print a register report as CSV
Improved:
-
balancesheet: now shows just assets and liabilities, not equity
-
print: comment positions (same line or next line) are now preserved
-
queries:
amtnow uses the = operator by default, egamt:50is equivalent toamt:=50 -
command line processing has been overhauled and made more consistent, and now has tests and debug output. More flags now work both before and after COMMAND:
-f,--rule-file,--alias,--help,--debug,--version. Command line help, command aliases, API docs and code have been improved. -
--debugnow takes an optional numeric argument to set the debug level higher than 1, for more verbose debug output in a few cases.
Fixed:
-
csv: CSV data containing non-ascii characters is now supported
-
build with latest versions of dependencies (text, warp, http-conduit etc.)
Release contributors:
Marko Kocić, Max Bolingbroke, and a big welcome to first-time committer John Wiegley! :)
2013/7/10 hledger-web 0.21.3
- drop yesod-platform dependency, it is not worthwhile. The other
yesod dependencies are currently without version ranges, so cabal
install might require
--constraintto restrict them in some cases.
2013/6/23 hledger 0.21.3
- csv: fix wrong application of multiple assignments in a conditional block
2013/6/4 hledger 0.21.2
- web: fix a build failure
2013/6/3 hledger 0.21.1
- web: show proper Y-values in register chart (fixes #122)
- web: avoid trailing commas in register chart values, in case of trouble with IE
2013/6/1 hledger 0.21
Bugs fixed:
- parsing: don’t fail when a csv amount has trailing whitespace (fixes #113)
- web: don’t show prices in the accounts sidebar (fixes #114)
- web: show one line per commodity in charts. Needs more polish, but fixes #109.
- web: bump yesod-platform dependency to avoid a cabal install failure
Journal reading:
- balance assertions are now checked after reading a journal
web command:
- web: support/require yesod 1.2
- web: show zero-balance accounts in the sidebar (fixes #106)
- web: use nicer select2 autocomplete widgets in the add form
Documentation and infrastructure:
- add basic cabal test suites for hledger-lib and hledger
2013/5/4 hledger 0.20.0.1
- web: require at least version 1.1.7 of yesod-core to avoid a potential build error
- Update the bug tracker and source repository links on hackage
2013/5/1 hledger 0.20
Bugs fixed:
- balance: a 0.19 regression which showed wrong total balance with
--flathas been fixed (#94) - register: when
--date2is used, the register is now sorted by the secondary date - web: some missing static & template files have been added to the package, fixing cabal-dev and hackage builds (#97, #98)
- web: some hardcoded static urls have been fixed
- Dependencies and code have been updated to support the latest libraries and GHC versions. For now, hledger requires GHC 7.2+ and hledger-web requires GHC 7.4+.
Journal reading:
- DOS-style line-endings are now also supported in journal and rules files.
!is now accepted in the status field as well as*, like ledger- The actual date and effective date terminology has changed to primary date and secondary date.
Use
--date2to select the secondary date for reports. (--aux-dateor--effectiveare also accepted for ledger and backwards compatibility). - Per-posting dates are supported, using hledger tags or ledger’s posting date syntax
- Comment and tag handling has been improved
CSV reading:
- CSV conversion rules have a simpler, more flexible syntax.
Existing rules files will need to be updated manually:
- the filename is now
FILE.csv.rulesinstead ofFILE.rules FIELD-field Nis nowFIELD %N+1(or set them all at once with afieldsrule)base-currencyis nowcurrencybase-accountis nowaccount1- account-assigning rules:
add
ifbefore the list of regexps, add indentedaccount2before the account name
- the filename is now
- parenthesised amounts are parsed as negative
Querying:
- Use
code:to match the transaction code (check number) field - Use
amt:followed by<,=or>and a number N to match amounts by magnitude. Egamt:<0oramt:=100. This works only with single-commodity amounts (multi-commodity amounts are always matched). tag:can now match (exact, case sensitive) tag values. Egtag:TAG=REGEXP.
add comand:
- Transaction codes and comments (which may contain tags) can now be entered, following a date or amount respectively. (#45)
- The current entry may be restarted by entering
<at any prompt. (#47) - Entries are displayed and confirmed before they are written to the journal.
- Default values may be specified for the first entry by providing them as command line arguments.
- Miscellaneous UI cleanups
register command:
- The
--related/-rflag shows the other postings in each transaction, like ledger. - The
--width/-woption increases or sets the output width.
web command:
- The web command now also starts a browser, and auto-exits when unused, by default (“local ui mode”).
With
--server, it keeps running and logs requests to the console (“server mode”). - Bootstrap is now used for styling and layout
- A favicon is served
- The search field is wider
- yesod devel is now supported; it uses
$LEDGER_FILEor~/.hledger.journal - the
blaze_html_0_5build flag has been reversed and renamed toblaze_html_0_4
Add-ons:
- The hledger-interest and hledger-irr commands have been released/updated.
- hledger-chart and hledger-vty remain unmaintained and deprecated.
Documentation and infrastructure:
- The hledger docs and website have been reorganised and updated
- Manuals for past releases are provided as well as the latest dev version
- hledger has moved from darcs and darcs hub to git and github (!)
- The bug tracker has moved from google code to github
- Feature requests and project planning are now managed on trello
- A build bot builds against multiple GHC versions on each commit
Release contributors:
- Sascha Welter commissioned register enhancements (
--relatedand--width) - David Patrick contributed a bounty for add enhancements
- Joachim Breitner added support for ! in status field
- Xinruo Sun provided hledger-web build fixes
- Peter Simons provided hledger-web build fixes, and a build bot
- Marko Kocić provided hledger-web fixes
2012/11/24 hledger-web 0.19.3
- web: fix “Prelude.read: no parse” errors with GHC >= 7.6
- web & lib refactoring
2012/11/16 hledger-web 0.19
- builds with yesod 1.1.3
- obeys command-line query options at startup again
- the autogenerated session file is now a dot file (.hledger-web_client_session.aes)
2012/11/16 hledger 0.19.1
- 87: fix an arithmetic and transaction balancing bug with multiple total-priced amounts ( @@ PRICE )
- parsing: ignore ledger-style balance assertions ( = BAL ) and fixed lot price declarations ( {= PRICE} )
2012/10/21 hledger 0.19
a much faster balance command, and support for the latest GHC and libs. mail
-
hledger, hledger-lib: support GHC 7.6 and latest cmdargs, haskeline, split
-
balance report no longer has an O(n^2) slowdown with large numbers of accounts, and is generally more speedy. Benchmark on a 2010 macbook:
+-------------------------------------------++--------------+--------------+--------+ | || hledger-0.18 | hledger-0.19 | ledger | +===========================================++==============+==============+========+ | -f data/100x100x10.journal balance || 0.21 | 0.07 | 0.09 | | -f data/1000x1000x10.journal balance || 10.13 | 0.47 | 0.62 | | -f data/1000x10000x10.journal balance || 40.67 | 0.67 | 1.01 | | -f data/10000x1000x10.journal balance || 15.01 | 3.22 | 2.36 | | -f data/10000x1000x10.journal balance aa || 4.77 | 4.40 | 2.33 | +-------------------------------------------++--------------+--------------+--------+ -
build version is set with CPP instead of cabal-file-th
2012/7/7 hledger 0.18.2
- web: fix compilation error with -fblaze_html_0_5 flag
- bump base lower bound to 4.3 to enforce GHC 7 requirement
2012/6/29 hledger 0.18.1
- register, print: fix reverse ordering of same-day transactions
- balance: respect all query terms, not just acct
- combine command-line flags like
--depthproperly with non-flag query patterns - web: don’t auto-create a missing journal file at startup
- stats: list included journal files
- support tilde (~) in journal and rules file paths
- expose more utilities from CsvReader
- remove ensureRulesFile debug trace
2012/5/29 hledger 0.18
- web: hledger-web is now based on yesod 1.0
- web: fix js error breaking second use of add form (#72)
- web: make
yesod develwork - the command-line now supports a more powerful query language, consistent with the web UI
- hledger now fully supports tags (aka metadata) on both transactions and postings, and querying by tag or tag value
- new commands
incomestatement,balancesheet, andcashflowprovide basic financial statements under certain conditions - format conversion is now done on demand, and the convert command has been dropped. So instead of
hledger convert FILE.csvjust dohledger -f FILE.csv printor any other command. You can also pipe any supported format intohledger -f- CMDand hledger will try to do the right thing. - support for GHC 6.12 has been dropped; this release has been tested with GHC 7.0.4, 7.2.2, and 7.4.1
- unicode is now handled properly on all supported GHC versions
- API and internal cleanups
2012/3/3 hledger-web 0.17.1
- set more upper bounds to fix cabal install issues with latest packages
2012/2/1 hledger 0.17
fixes bugs and updates dependencies mail
- support HP 2011.4.0.0
- support and require cmdargs 0.9
- allow non-threaded builds, supporting more debian architectures
- parsing: give a clearer error when journal file path contains ~
- parsing:
-B/--costnow ignores P historical prices, like ledger - parsing: inferred amounts now use the cost commodity if known, like ledger (#69)
- balance: report differently-priced lots in an account as a single amount, like ledger
- web: support and require yesod >= 0.9.4
- web: use the main aeson package again
- web: fix a regression with dollar signs in hamlet templates
- web: add form allowed blank account names (#81)
- chart, vty: hledger-chart and hledger-vty demoted to non-maintained extras for now
2011/10/26 hledger-web 0.16.5
- web: fix a ghc 6.12 incompatibility in Settings.hs
2011/10/24 hledger-web 0.16.4
- web: yet another cabal install fix, fix AppConfig name clash
2011/10/4 hledger-web 0.16.3
- web: another cabal install fix, disable favicon.ico since it’s not easily embeddable
2011/10/4 hledger-web 0.16.2
- web: more cabal install fixes (remove bad path, add routes and models) (#63)
2011/10/4 hledger 0.16.1
- parsing: show correct line number for posting parse errors (#67)
- web: declare static files as extra-source-files to fix cabal install (#63)
- web: add a threaded flag for debian (#68)
- web: fewer build warnings by default
2011/10/1 hledger 0.16
a stability/bugfix/polish release (which may become the pattern for even-numbered releases in future.) mail
- cli: strip the – when calling add-on commands, so their options work (#64)
- cli: hledger ADDON
--versionnow shows add-on command’s version - cli: only the add and web commands auto-create the journal file
- cli: give a non-confusing error if LEDGER_FILE contains a literal tilde
- add: clearer prompts, more validation, use . to end also
- add: use unix line endings consistently, avoiding parse error on windows (#51)
- add: avoid excess whitespace between transactions (#46)
- balance: ledger compatibility fix: don’t elide parent accounts with multiple displayed subaccounts
- convert: always order converted transactions by date
- convert: rename currency -> base-currency, in-field, out-field -> amount-in-field, amount-out-field
- convert: give an error, not a zero when date or amount-in-field/amount-out-field parsing fails
- register: show more useful range of intervals with
--emptyand a query pattern - print, web: always show both dates, ignoring
--effective(#42) - web: production builds (the default with cabal) have all web content embedded (dev builds use ./static/) (#63)
- web: update to yesod 0.9
- web: obey at least some of the general reporting options, like
--cost - web: adjust the default base url when a custom port is specified
- web: prevent an infinite redirect when custom base url has a trailing slash
- web: fix “not:‘multi word’” patterns
- web: hide old title and search form when adding/editing
- web: adjust
--helpto indicate command-line arguments are not expected - web: don’t bother running cli unit tests at startup
2011/9/12 hledger 0.15.2, hledger-web 0.15.3
- handle multiple filter patterns on the command-line again
- don’t pass an add-on command’s name to it as an extra argument
- don’t give a confusing error with
-fand no command - fix a regression balancing a transaction containing different prices
- web: fix journal edit form
- web: fix wrong transaction amount in account register with virtual postings
- web: fix some invalid html
2011/9/2 hledger 0.15.1, hledger-web 0.15.2
- fix a parsec 2 incompatibility
- web: add missing Hledger.Web.Options to cabal file
- web: tighten up dependencies to reduce build problems
2011/9/1 hledger 0.15
- hledger’s options are now modal, providing better help (using cmdargs)
- hledger now lists and runs any hledger-* add-ons found in the user’s path
- case insensitivity of filter patterns has been fixed
- parsing:
alias/end aliasesdirectives, for renaming accounts, are supported, like ledger’s but a bit more powerful; also an--aliasoption for renaming on the fly - parsing: the
accountdirective now preserves posting type (normal/virtual/balanced virtual) - parsing: the
popdirective is supported as an alias forend tag, like ledger - parsing:
P(historical price) directives can contain a (ignored) numeric time zone, like ledger - parsing: the leading
!in directives is now optional and deprecated, like ledger - parsing: entries with a negative amount in the first posting now infer the correct balancing amount
- parsing: bad date checking is more accurate
- balance: collapsing of boring accounts to one line can be disabled with
--no-elide - balance: fix a wrong precision regression from last release
- convert: standard input can be converted
- convert: an alternate rules file can be specified with
--rules - convert:
account2-fieldcan be used when the CSV file specifies both accounts - convert:
description-fieldcan have a custom format and combine multiple CSV fields - convert:
in-fieldandout-fieldsupport CSV files that use two amount columns - convert: don’t fail when there’s no default journal file
- web: the web interface has been overhauled/cleaned up
- web: account register views are now transaction-based, like gnucash etc., and show accurate historical balances when possible
- web: simple balance charts are displayed (using flot)
- web: more expressive and consistent search patterns, using a new matching engine
- web: add form uses currently focussed account as default, redirects to itself, formats status messages better
- web: sidebar now shows empty/boring accounts too
- web: now uses warp and a newer yesod
- api simplifications
- importable Hledger, Hledger.Web, Hledger.Vty and Hledger.Chart modules
- the basic reports are now provided by hledger-lib for easier reuse
- new api use examples:
equity.hs,uniquify.hs - some old base 3 support has been dropped
- the old
-sflag has been dropped
2011/4/22 hledger 0.14
- remove the specific process dependency that caused too many cabal install problems
- treat arguments as possibly-encoded platform strings, do not assume UTF-8
- hledger now always reads and writes data as UTF-8, ignoring the system locale (#34)
- look at the LEDGER_FILE env var for the journal path, otherwise LEDGER, like ledger
- handle a blank LEDGER_FILE or LEDGER value more gracefully (use the default file path)
- the default journal file path is now ~/.hledger.journal, to avoid breaking mac filevault (#41)
- amounts with different prices are now aggregated, like ledger
- zero amounts now have no sign or commodity, like ledger
- parsing: assume current year when transaction dates have no year and there is no default year
- parsing: more careful validation of eg leap years in transaction dates
- parsing: better international number format support, allowing comma as decimal point and flexible digit groups (#32)
- parsing: support @@ syntax specifying total price
- parsing: infer the conversion price in transactions involving two unpriced commodities
- parsing: support per-posting cleared status
- parsing: more reporting interval syntax: biweekly, bimonthly, every N days/weeks/months/quarters/years, every Nst/nd/rd/th day of month/week
- add: avoid offering account names for completion in inappropriate contexts
- add: remember default account even if user submits a different amount.
- convert: account-field directive specifies a field containing the base account name
- convert: effective-date-field directive specifies a field containing the effective date
- convert: date-format directive specifies custom date formats
- convert: allow amount fields containing “AMT @@ PRICE”
- histogram: honour the specified start or end dates
- print: don’t show a trailing space when description is blank
- web: allow filter patterns with spaces if quoted, like command line
- web: make edit form more cross-browser compatible, fixing it in firefox (#38)
- web: move hidden add/edit/import forms below main content to help text-mode browsers a bit (#33)
Release contributors: Simon Michael, Dmitry Astapov, Eric Kow, Max Bolingbroke, Omari Norman. Stats: 137 days, 113 commits, 11 end-user features and 15 end-user bugfixes since last release. 189 unit & functional tests and 59% unit test coverage (hledger, hledger-lib packages). 5540 lines of code (all packages).
2010/12/6 hledger 0.13
readline editing and tab completion from Judah Jacobson, more ledger compatibility, a more robust and installable web interface, bugfixes, and a much-deliberated package split. mail
- move web, vty, chart commands into separate hledger-web, hledger-vty, hledger-chart packages. This both simplifies (no more build flags) and complicates (more room for dependency hassles), but I hope overall it will be easier and more scalable.
- all packages but chart are now marked “beta”, ie “not finished but suitable for everyday use”
- parsing: ledger compatibility: support D default commodity directive
- parsing: ledger compatibility: ignore metadata tags on transactions and postings
- parsing: ledger compatibility: ignore cleared flags at the start of postings
- parsing: ledger compatibility: ignore C commodity conversion directives
- parsing: price precisions no longer affect commodities’ display precisions
- add: readline-style editing
- add: tab-completion for account names
- add: add the default commodity, if any, to commodity-less amounts (#26)
- add: misc. commodity/precision/defaults-related bugfixes
- chart: give a meaningful error message for empty journals
- chart: update for current Chart lib (0.14)
- web: support files now live in ./.hledger/web/ and will be auto-created at startup
- web: page layout is more robust with wide content
- web: allow editing of included files
- web: handle multiple filter patterns correctly
- web: allow single- or double-quoted filter patterns containing spaces
- web: update for current yesod lib (0.6.*)
- transaction balancing is now based on display precision (#23)
- briefer, more informative usage error messages
2010/9/6 hledger 0.12.1
- web: fix account filtering breakage
- installing: tighten up utf8-string dependency
2010/9/5 hledger 0.12
- web: new, better web ui; accounts are now a permanent sidebar; add form uses auto-completing combo fields
- installing: fix a build error with parsec 3 (#22)
- installing: require exactly matching hledger-lib version for more robust builds
- installing: explicit data-object dependency to ensure hledger and hledger-lib use the same time version
- installing: explicit hamlet dependency for more robust building
- installing: build threaded and with warnings
- installing: drop -fweb610 flag
- installing: add gtk2hs-buildtools dependency needed to build with -fchart
- installing: require cabal 1.6 or greater
- add
-D/--dailyflag - register: with
--depth, clip account names or aggregate postings rather than excluding them - fix !include with deeply nested directories (#21)
- fix obscured date parse errors with parsec 3
- handle unicode better in errors
- fix a ghc 6.12.3 error when running interpreted
Stats: 50 days and 90 commits since last release, now at 5741 lines of code with 136 tests and 41% unit test coverage.
2010/07/17 hledger 0.11.1
- fix
--versionoutput
2010/07/17 hledger 0.11
- split
--help, adding--help-optionsand--help-all/-H, and make it the default command - use “journal” instead of “ledger file”; default suffix is .journal, default file is ~/.journal
- auto-create missing journal files rather than giving an error
- new format-detecting file reader (mixed journal transactions and timelog entries are no longer supported)
- work around for first real-world rounding issue (test zero to 8 decimal places instead of 10)
- when reporting a balancing error, convert the error amount to cost
- parsing: support double-quoted commodity symbols, containing anything but a newline or double quote
- parsing: allow minus sign before commodity symbol as well as after (also fixes a convert bug)
- parsing: fix wrong parse error locations within postings
- parsing: don’t let trailing whitespace in a timelog description mess up account names
- add: allow blank descriptions
- balance:
--flatprovides a simple non-hierarchical format - balance:
--dropremoves leading account name components from a--flatreport - print, register, balance: fix layout issues with mixed-commodity amounts
- print: display non-simple commodity names with double-quotes
- stats: layout tweaks, add payee/description count
- stats: don’t break on an empty file
- stats:
-p/--periodsupport; a reporting interval generates multiple reports - test: drop verbose test runner and testpack dependency
- web: a new web ui based on yesod, requires ghc 6.12; old ghc 6.10-compatible version remains as -fweb610
- web: allow wiki-like journal editing
- web: warn and keep running if reloading the journal gives an error
- web:
--portand--base-urloptions set the webserver’s tcp port and base url - web: slightly better browser opening on microsoft windows, should find a standard firefox install now
- web: in a web-enabled build on microsoft windows, run the web ui by default
Stats: 55 days and 136 commits since last release. Now at 5552 lines of code with 132 tests and 54% unit test coverage.
2010/05/23 hledger 0.10
installation and bug fixes and api improvements mail
- fix too-loose testpack dependency, missing safe dependency
- fix ghc 6.12 compatibility with -fweb
- fix handling of non-ascii arguments with ghc 6.12
- fix “0.8” in
--versionoutput - fix an occasional stack overflow error due to infinite recursion in Posting/Transaction equality tests
- the -fwebhappstack build flag is gone for now, to avoid a cabal problem
- parsing: if there is no description, don’t require a space after the transaction date
- parsing: balance balanced-virtual postings separately, allow them to have an implicit amount
- parsing: timelog entries now generate balanced transactions, using virtual postings
- parsing: simpler high-level parse error message
- parsing: clearer bad date errors
- add: fix wrongful program exit on bad dates
- print: negative account patterns now exclude transactions containing any posting to a matched account
- vty: rename the ui command to vty for consistency
- vty: fix restricted account scope when backing up to top level
- web: fix non-ascii handling with ghc 6.12
- web: fix a bug possibly affecting reload-on-change
- consolidate module namespace under Hledger, api cleanups
Stats: 44 days, 81 commits since last release. Now at 4904 lines of code including tests, 144 tests, 53% coverage.
2010/04/10 hledger 0.9
many bugfixes and small improvements, GHC 6.12 support, and a separate library package to make building (h)ledger-compatible tools easier. mail
- ghc 6.12 support
- split off hledger-lib package, containing core types & utils
- parsing: ignore D, C, N, tag, end tag directives; we should now accept any ledger 2.6 file
- parsing: allow numbers in commodities if double-quoted, like ledger
- parsing: allow transactions with empty descriptions
- parsing: show a better error for illegal month/day numbers in dates
- parsing: don’t ignore trailing junk in a smart date, eg in web add form
- parsing: don’t ignore unparsed text following an amount
- parsing: @ was being treated as a currency symbol
- add: fix precision handling in default amounts (#19)
- add: elide last amount in added transactions
- convert: keep original description by default, allow backreferences in replace pattern
- convert: basic csv file checking, warn instead of dying when it looks wrong
- convert: allow blank/comment lines at end of rules file
- print: always show zero amounts as 0, hiding any commodity/decimal places/price, like ledger
- register: fix bad layout with years < 1000
- register: fix a Prelude.head error with reporting interval,
--empty, and--depth - register: fix a regression, register should not show posting comments
- register: with
--empty, intervals should continue to ends of the specified period - stats: better output when last transaction is in the future
- stats: show commodity symbols, account tree depth, reorder slightly
- web: -fweb now builds with simpleserver; to get happstack, use -fwebhappstack instead
- web: pre-fill the add form with today’s date
- web: help links, better search form wording
- web: show a proper error for a bad date in add form (#17)
- web: fix for unicode search form values
- web: fix stack overflow caused by regexpr, and handle requests faster (#14)
- web: look for more-generic browser executables
- web: more robust browser starting (#6)
- error message cleanups
- more tests, refactoring, docs
Stats: 58 days, 2 contributors, 102 commits since last release. Now at 3983 lines of non-test code, 139 tests, 53% coverage.
2010/02/11 hledger 0.8
Bug fixes, refactoring and Hi-Res Graphical Charts. mail
- parsing: in date=date2, use first date’s year as a default for the second
- add: ctrl-d doesn’t work on windows, suggest ctrl-c instead
- add:
--no-new-accountsoption disallows new accounts (Roman Cheplyaka) - add: re-use the previous transaction’s date as default (Roman Cheplyaka)
- add: a command-line argument now filters by account during history matching (Roman Cheplyaka)
- chart: new command, generates balances pie chart (requires -fchart flag, gtk2hs) (Roman Cheplyaka, Simon Michael)
- register: make reporting intervals honour a display expression (#18)
- web: fix help link
- web: use today as default when adding with a blank date
- web: re-enable account/period fields, they seem to be fixed, along with file re-reading (#16)
- web: get static files from the cabal data dir, or the current dir when using make (#13)
- web: preserve encoding during add, assuming it’s utf-8 (#15)
- fix some non-utf8-aware file handling (#15)
- filter ledger again for each command, not just once at program start
- refactoring, clearer data types
Stats: 62 days, 2 contributors, 76 commits since last release. Now at 3464 lines of non-test code, 97 tests, 53% test coverage.
2009/12/11 hledger 0.7
- price history support (first cut): P directives now work,
though differently from ledger. Each posting amount takes its
fixed unit price from the price history (or
@) when available. This is simple and useful for things like
foreign currency expenses (but not investment tracking). Like
ledger, balance and register don’t show amount prices any more, and
don’t separate differently-priced amounts. Unlike ledger, print
shows all amount prices, and supports
-B. --effectiveoption, will use transactions’ effective dates if any- convert: new rules file format, find/create rules file
automatically, more robust parsing, more useful
--debugoutput - print: always sort by date, fix long account name truncation, align amounts, show end of line comments, show all amounts for clarity (don’t elide the final balancing amount)
- ui: use vty 4, fixes non-ascii and gnome terminal problems (issues #3, #4)
- web: allow data entry, react to data file changes, better layout, help links, remove histogram command and filter fields for now, fix bad localhost redirect, filter form did not work in eg firefox (issue #7), reset link did not work in all browsers
- parsing: require whitespace between date and status code, allow (and ignore) a time in price records, better error messages, non-zero exit code on parse failure
- display non-ascii error messages properly (issue #5)
- fix an arithmetic bug that occasionally rejected valid transactions
- fix a regex bug in showtree
- don’t break if HOME is undefined
--debugnow implies--verbose- add functional tests like ledger’s, use test-framework for speedy running, release shelltestrunner as a separate package
- many hlint cleanups (Marko Kocić)
- many site and documentation updates
Stats: 60 days, 1 contributor, 50 commits since last release. Now at 3377 lines of non-test code, 97 tests, 53% test coverage.
2009/06/22 hledger 0.6.1
- avoid use of exitSuccess which was breaking ghc 6.8/base 3 compatibility (issue #2)
2009/06/13 hledger 0.6
Some pre-built binaries are now available. cabal install works on gnu/linux, mac and windows. Hurrah! mail
- now cabal-installable on unix, mac, and windows, with Haskell Platform
- provide experimental platform binaries
- parsing: fix a silly failure to open ledger file paths containing ~
- parsing: show better errors for unbalanced transaction and missing default year
- parsing: allow parentheses and brackets inside account names, as ledger does
- parsing: fail on empty account name components, don’t just ignore
- add: description passed as arguments now affects first transaction only
- add: better handling of virtual postings and default amounts
- print, register: show virtual accounts bracketed/parenthesised
- web: improved web ui supporting full patterns & period expressions
- new “stats” command reports some ledger statistics
- many dev/doc/deployment infrastructure improvements
- move website into darcs repo, update home page
- move issue tracker to google code
Release stats:
- Contributors: Simon Michael
- Days since last release: 21
- Commits: 94
- Lines of non-test code: 2865
- Tests: 82
- Test coverage: 53% expressions
- Known errors: 3 (inconsistent eliding, vty-related failures)
- Performance: similar (http://hledger.org/profs/200906131120.bench)
2009/05/23 hledger 0.5.1
- two fixes: really disable vty flag by default, and include ConvertCommand in cabal file
2009/05/23 hledger 0.5
- the vty flag is disabled by default again, to ease installation on windows
- use ledger 3 terminology: a ledger contains transactions which contain postings
- new “add” command prompts for transactions interactively and adds them to the ledger
- new “convert” command transforms bank CSV exports to ledger format, with rule-based cleanup
- new “histogram” command shows transaction counts per day or other reporting interval
- most commands now work properly with UTF8-encoded text (Sergey Astanin)
- invoking as “hours” is now less different: it just uses your timelog, not your ledger
--quarterly/-Qoption summarises by quarter--uncleared/-Uoption looks only at uncleared transactions- be more accurate about checking balanced amounts, don’t rely on display precision
- enforce balancing for bracketed virtual postings
- fix bug in eliding of posting amounts
- don’t show trailing spaces on amountless postings
- parse null input as an empty ledger
- don’t treat comments as part of transaction descriptions
- require some postings in ledger transactions
- require a non-empty description in ledger transactions
- don’t fail when matching an empty pattern, as in “not:”
- make the web server handle the null path
- code, api and documentation updates
- add a contributor agreement/list
Release stats:
- Contributors: Simon Michael, Sergey Astanin
- Days since last release: 51
- Commits: 101
- Lines of non-test code: 2795
- Tests: 76
- Known errors: 0
2009/04/03 hledger 0.4
There is also a new website at hledger.org, with screenshots (textual!), a demo (will it survive!?), and docs (not too many!) … I wrote it because I did not want to hack on c++ and because haskell seemed a good fit … new happstack-based web interface. mail
- new “web” command serves reports in a web browser (install with -f happs to build this)
- make the vty-based curses ui a cabal build option, which will be ignored on MS windows
- drop the
--options-anywhereflag, that is now the default - patterns now use
not:anddesc:prefixes instead of^and^^ - patterns are now case-insensitive, like ledger
!includedirectives are now relative to the including file (Tim Docker)- “Y2009” default year directives are now supported, allowing m/d dates in ledger
- individual transactions now have a cleared status
- unbalanced entries now cause a proper warning
- balance report now passes all ledger compatibility tests
- balance report now shows subtotals by default, like ledger 3
- balance report shows the final zero total when
-Eis used - balance report hides the final total when
--no-totalis used --depthaffects print and register reports (aggregating with a reporting interval, filtering otherwise)- register report sorts transactions by date
- register report shows zero-amount transactions when
-Eis used - provide more convenient timelog querying when invoked as “hours”
- multi-day timelog sessions are split at midnight
- unterminated timelog sessions are now counted. Accurate time reports at last!
- the test command gives better
--verboseoutput --versiongives more detailed version numbers including patchlevel for dev builds- new make targets include: ghci, haddocktest, doctest, unittest, view-api-docs
- a doctest-style framework for functional/shell tests has been added
Release stats:
- Contributors: Simon Michael, Tim Docker; thanks to the HAppS, happstack and testpack developers
- Days since release: 76
- Commits: 144
- Lines of non-test code: 2367
- Tests: 56
- Known errors: 0
2009/01/17 hledger 0.3
- count timelog sessions on the day they end, like ledger, for now
- when options are repeated, use the last instead of the first
- builds with ghc 6.10 as well as 6.8
- a simple ui for interactive report browsing: hledger ui
- accept smart dates everywhere (YYYYMMDD, Y/M/D, Y, M/D, D, jan, today, last week etc.)
--period/-pflag accepting period expressions like “in 2008”, “weekly from last month”..-W/-M/-Yconvenience flags to summarise register weekly, monthly, yearly--depthand-Eflags also affect summarised register reports (including depth=0)--display/-dflag supporting date predicates (like “d<[DATE]”, “d>=[DATE]”)!includedirective to include additional ledger files!accountdirective to set a default parent account- Added support for reading historical prices from files
- timelog and ledger entries can be intermixed in one file
- modifier and periodic entries can appear anywhere (but are still ignored)
- help and readme improvements
- runs much faster than 0.2
Release stats:
- Contributors: Simon Michael, Nick Ingolia, Tim Docker; thanks to Corey O’Connor & the vty team
- Lines of non-test code: 2123
- Tests: 58
- Known errors: 1
2008/11/23 hledger 0.2
- fix balance report totals when filtering by account
- fix balance report selection of accounts when filtering by account
- fix a bug with account name eliding in balance report
- if we happen to be showing a not-yet-auto-balanced entry, hide the AUTO marker
- fix print command filtering by account
- omit transactions with zero amount from register report
- Fix bug in parsing of timelogs
- rename
--showsubsto--subtotal, like ledger - drop
--usageflag - don’t require quickcheck
- priced amounts (eg “10h @ $50”) and
--basis/--cost/-Bflag to show them with cost basis - easy
--depthoption, equivalent to ledger’s-d 'l<=N' - smarter y/m/d date parsing for
-band-e(any number of digits, month and day default to 1, separator can be / - or .) -nflag for balance command--empty/-Eflag- build a library, as well as the exe
- new home page url (http://joyful.com/hledger)
- publish html and pdf versions of README
- detect display preferences for each commodity like ledger
- support amounts with multiple currencies/commodities
- support
--real/-Rflag - support
-C/--clearedflag to filter by entry status (not transaction status) - support virtual and balanced virtual transactions
- parse comment lines beginning with a space, as from M-; in emacs ledger-mode
- allow any non-whitespace in account names, perhaps avoiding misleading missing amounts errors
- clearer error message when we can’t balance an entry
- when we fail because of more than one missing amount in an entry, show the full entry
- document the built-in test runner in
--help - add a
--verbose/-vflag, use it to show more test-running detail
Release stats:
- Contributors: Simon Michael, Tim Docker
- Lines of non-test code: 1350
- Tests: 43
- Known errors: 0
2008/10/15 hledger 0.1
mail I’m pleased to announce the first release of hledger, a command-line accounting tool similar to John Wiegley’s c++ ledger. hledger generates simple ledger-compatible transaction & account balance reports from a plain text ledger file. It’s simple to use, at least for techies. This has been my “learning Haskell” project, but I think it’s also useful. It is much less featureful than ledger, and not quite as fast, but it has the virtue of being fun for haskellers to hack on. I am documenting the code, the app is simple, and I’m not too far up the haskell learning curve, so I think other people learning haskell might enjoy a look. It is currently ~1100 lines of haskell excluding tests. My thanks to John Wiegley for help with compatibility and for his very useful ledger tool. I use it (and now, both of them) daily to track time and money. This is of course a hot topic around our planet. I hope you find it useful or intriguing.
Release stats:
- Contributors: Simon Michael
Docs
hledger has a lot of documentation, to suit different needs. Docs can also be accessed from the left sidebar.
Reference
The manuals are thorough and authoritative.
Do at least skim their tables of contents so you’ll know where to look in future.
You can also view the manuals locally with commands like
hledger help, hledger CMD --help, hledger-ui --man, hledger-web --info, man hledger-ui, info hledger-web:
See also:
Too long, didn’t read
You can get very brief help with a tldr client or in your browser:
- hledger
- hledger add
- hledger accounts
- hledger add
- hledger aregister
- hledger balance
- hledger balancesheet
- hledger import
- hledger incomestatement
- hledger print
- hledger-ui
- hledger-web
Quick starts
Fast introductions for folks comfortable with command line tools.
- Home: A quick example
- 2 minute quick start
- 5 minute quick start
- 10 minute quick start
- hledger manual: Common tasks
- hledger manual: Journal cheatsheet
- Workflows
- hledger and Ledger, for Ledger users
- hledger and Beancount, for Beancount users
Tutorials
Beginner friendly, step by step guides.
- Accounting basics for PTA users
- Tutorial: hledger add
- Tutorial: hledger-ui
- Tutorial: hledger-web
- Tutorial: Import CSV data
- Tips for learning hledger
hledger by example
A growing book introducing hledger one step at a time.
- hledger by example
- Get hledger installed
- Check your setup
- Start a journal
- Add a transaction: starting balances
- Journal entries
- Account names
- More transactions
- Overview of commands
- print: show whole transactions
- register: show detailed changes
- balance: show account totals
- balancesheet: assets and liabilities
- incomestatement: revenues and expenses
- Reading CSV files
- Importing new transactions
- Reading timeclock files
- Reading timedot files
Videos
Short videos:
-
Hledger in 10 Minutes: The Ultimate CLI Tool (2025, 5m)
-
hledger fan’s beginner lessons (2019-2021, ~3m)
- The Basics: An hledger Transaction
- hledger Basics: 3 Must-Know Options for the Balance Command
- The Basics: The hledger Register Command
- hledger Basics: Focus on Months
- hledger basics: save keystrokes!
- hledger: looking under the hood
- hledger: untie your data with “nots”
- hledger: fun and profit with incomestatement
- hledger: fast fancy formatting
- hledger: who did you pay?
- hledger: bring order to your expenses
- hledger: commands to help with reconciling7
- hledger: shortcuts part 1
- hledger: shortcuts part 2
- hledger: shortcuts part 3
- hledger add
- hledger iadd
Longer talks:
- Youngbin Han @ FOSS for All
(2025, slides, korean and english)
Plain Text Accounting with hledger
with short videos of add and web - Glenn Ramsey @ Kiwi Pycon XI (2022, 30m)
Plain text accounting for fun and profit - Simon Michael @ HaskellerZ meetup (2021, 2h)
Inside hledger: an architectural tour and how-to, slides - Conversations in Code, Episode 3: Introduction to Plain Text Accounting (2020, 1h, audio only), discussion
- Intproject Mi: Учет сделок с проводками в текстовом формате (2019, 34m)
- Evolution of hledger (Gource visualization) (2019, 38m)
- Simon Michael @ Brainium meetup (2016, 2h)
Hands-on with hledger - Chris Vollick @ DevHouse Waterloo meetup (2016, 40m)
hledger, an open source accounting tool - FLOSS Weekly episode 375: hledger (2016) - overview (3m), full interview (1h)
See also:
Articles
A few notable posts:
- Amitai Burstein: Why We Chose Plain Text Accounting with Hledger - getting started; hledger-ui
- Teo Si-Yan: Making sense of your finances with hledger reports - basic reports
- Matt Maguire: Plain Text Accounting with Emacs, part 2, part 3
Cookbook
Additional docs, tips and rough notes. If you don’t find what you’ll looking for here, try also:
- Plain Text Accounting Cookbook
- PTA forum: hledger tag
- Reddit: plaintextaccounting: “hledger”
- Support
USING HLEDGER
Checking for errors
Customising
Data entry
- Create a journal
- Getting prices
- How to record journal entries
- How to figure out journal entries
- Importing CSV data
- Cost notation
Examples
- examples/ - examples and snippets, old and new
- examples/csv/ - hledger CSV rules library
Making charts
Other software
- Exporting from hledger
- hledger and Beancount
- hledger and dsq / DataStation
- hledger and just
- hledger and GnuCash
- hledger and Ledger
- hledger and Obsidian
- hledger and Paisa
- hledger and Postgres
- hledger and Quicken/Quickbooks
- hledger and SimpleFIN
- hledger and SQLite
- hledger and Ultorg
- hledger and YNAB
Preserving your data
- Track changes with version control
- Backups, the 3-2-1 rule
Reporting
- Making sense of your finances with hledger reports
- Report examples
- Reporting version control stats
- Rewrite account names
- Rewrite commodity symbols
- Tags tutorial
Scripts and add-ons
Setups and workflows
User interfaces
ACCOUNTING
Accounting and bookkeeping
- Accounting basics for PTA users
- Accounting: further study
- Common journal entries
- PTA: Choosing cash or accrual
Borrowing and lending
Budgeting
Eco accounting
Forecasting
FOSS projects
Inventory tracking
Investing and trading
- PTA Cookbook: Investing and trading
- hledger manual: Cost reporting
- hledger manual: Value reporting
- hledger manual: balance features
- Track investments (2017)
- Track investments (2020)
- Calculate unrealized gain
- Calculate return on investment
- hledger-lots
- Average cost capital gains with hledger 2023
- Track foreign stock capital gains using hledger 2023
- Full-fledged hledger: Manual lot tracking 2024
Invoicing
Multiple currencies
- Multicurrency tutorial (2018)
- Currency conversion (2021)
- hledger manual: Cost reporting
- hledger manual: Value reporting
Non-profit accounting
Taxes
Time tracking
Trip expenses
Shared expenses
Support
Main discussion channels:
| #hledger matrix chat | active chat room, the best place for getting quick help |
| #hledger IRC chat | less featureful chat |
| hledger mail list | usually fairly quiet |
| Mastodon #hledger | quick and with a wider reach |
| hledger bug tracker | for the hledger software |
| website bug tracker | for the hledger.org website |
| PTA forum | good for longer hledger/PTA content and searchability |
| PTA reddit | consider using the forum instead |
Chat
The #hledger matrix chat is the best place for quick help and feedback. (Element is a good client.)
The #hledger IRC chat is older but also available.
The #plaintextaccounting matrix chat and #plaintextaccounting IRC chat discuss all PTA apps.
Mail list
The hledger mail list is on google groups, low traffic with about 200 subscribers.
Read: list.hledger.org
Join: [email protected]
Send: [email protected]
Issue tracker
hledger issues are tracked in the hledger repos on github. There are some shortcut urls:
bugs.hledger.org (bugs only)
wishes.hledger.org (wishes only)
issues.hledger.org (all issues)
issues.hledger.org/new (report a new issue)
prs.hledger.org (all pull requests)
open issues by category
website issues
finance issues
Maintainer
[email protected]
Mastodon
#hledger,
hledger,
#plaintextaccounting,
plaintextaccounting
Bluesky
#hledger,
hledger,
#plaintextaccounting,
plaintextaccounting
Twitter / X.com
#hledger,
hledger,
#plaintextaccounting,
plaintextaccounting
xcancel.com:
#hledger,
hledger,
#plaintextaccounting,
plaintextaccounting
Hacker News
stories and
comments
Youtube
“hledger” videos
(or plaintextaccounting.org’s list)
Stack Exchange
[hledger] on money.stackexchange.com
PTA website
plaintextaccounting.org introduces plain text accounting generally.
PTA forum
forum.plaintextaccounting.org is a comfortable place for long-term PTA discussion.
PTA reddit
reddit.com/r/plaintextaccounting is reasonably active, but consider using the forum instead for better UX and content rights.
Scripts and add-ons
(This is the README in the hledger repo’s bin/ directory,
also published as the Scripts and add-ons page on hledger.org.)
A script is a program you can run immediately without needing to compile it first. They are often small and defined in a single file or shell alias or shell function. You can create your own simple or complex scripts which enhance hledger. Eg you might script a complicated report so you don’t have to remember the detailed command(s).
A hledger add-on command is any program whose name begins with “hledger-”.
Add-on commands found in PATH will appear in the commands list (shown when you run hledger with no arguments).
Some of the scripts below are add-ons.
Some add-ons are written in Haskell and can use hledger’s full power, like builtin commands.
Below are some existing scripts you can use or learn from. Most of these are collected in hledger’s bin/ directory, which you can get by cloning the hledger source. Compiled add-ons are also listed below, in their own section.
Note, you don’t need any of these extras if you are new to hledger - except possibly hledger-ui and hledger-web, which can be nice to have at the start.
Related scripts
Here are some scripts which don’t use hledger directly, but might be useful to hledger users. (For more, see also: plaintextaccounting.org > Software).
ledgereval
ledgereval
is a helper for evaluating Ledger value expressions.
It’s useful eg when converting amount expressions like (1 USD + 2 USD) to simple amounts.
pricehist
pricehist is useful for downloading market prices / conversion rates; recommended.
And hledger-pricehist
is a small script to make it show up in the hledger commands list.
paypaljson
paypaljson
downloads the last 30 days of Paypal transactions (requires a free developer account & API key).
paypalcsv
paypalcsv (python)
converts paypaljson’s output to CSV, with format similar to Paypal’s manually-downloaded CSV.
Example usage with hledger 1.50+:
call these in a CSV rules file like: source | paypaljson | paypalcsv.
Or, do the download externally with paypaljson >paypal.json,
then convert to CSV in the rules file: source | paypalcsv paypal.json.
simplefinsetup
simplefinsetup
helps set up access to SimpleFIN (simplefin.org), a developer-friendly aggregator of US bank data.
simplefinjson
simplefinjson
downloads data for one or more bank accounts from SimpleFIN’s API, as JSON.
simplefincsv
simplefincsv
converts SimpleFIN’s JSON data to CSV, for one or more bank accounts.
Example usage with hledger 1.50+:
download multi-account JSON once with simplefinjson >simplefin.json,
and in each account’s CSV rules file, extract that account’s CSV, eg:
source | simplefincsv simplefin.json 'wells fargo.*checking'.
See also hledger and SimpleFIN.
hledger command line scripts
These scripts use hledger’s command line interface, or process one of its output formats.
bashrc
bashrc
contains many example bash aliases and functions.
After installing the bin scripts: as a bash user,
# customise FINDIR and LEDGER_FILE at the top of bin/bashrc
$ . bin/bashrc
$ fin # list the scripts available
ft
ft
is a way to organise your finance-related reports and scripts using standard bash.
(See also Justfile below.)
$ alias f=~/src/hledger/bin/ft
$ f
--------------------------------------------------------------------------------"; }
ft - finance tool: run financial reports and finance-related scripts
Usage: ft [COMMAND [ARGS]]
Commands:
help show this help
get-csv download auto-downloadable CSVs (paypal)
import-dry import new downloaded transactions to the journal, dry run
import import new downloaded transactions to the journal, logging and not printing errors
get-prices [PRICEHISTFETCHOPTS] - download prices for main commodities (default: today's)
bs show balance sheet
is show income statement
a show assets
r show revenues
x show expenses
ab show assets bar chart
rb show revenues bar chart
xb show expenses bar chart
al show assets line chart
rl show revenues line chart
xl show expenses line chart
forecast print transactions predicted by forecast rules from last week on
household show a draft month-end household adjustment transaction for last month
consulting show consulting revenue
bin [PAT] show all scripts in $DIR/bin/[bashrc] (default: ~/finance/)
OTHERCMD [ARGS] run other hledger commands on the default journal
Add hledger options to customise reports.
tt
tt
is a similar bash multi-script for time reports.
$ alias t=~/src/hledger/bin/tt
$ t
--------------------------------------------------------------------------------
tt - time tool: run time reports and time-related scripts
Usage: tt [COMMAND [ARGS]]
Commands:
help show this help
dash show time dashboard, redisplaying when timelog files change
status show current time status
what what happened ? Show largest balances first, today and depth 1 by default
dots print line of N dots, grouped in 4s (suitable for timedot)
x horizontal time summary this year, monthly by default
y vertical time summary this year, monthly by default
rweeks recent weeks' time budgets
weeks this and last week's time budgets
hours show a bar chart of daily hours
accunused show unused / undeclared accounts
accunusedcat show unused / undeclared accounts by category
accadd add declarations for all undeclared accounts
budgets show monthly time budget performance this year
budgetsy show monthly time budget performance this year, vertically
budgetsw show weekly time budget performance this year
budgetswx show weekly time budget performance this year, horizontally
OTHERCMD [ARGS] run other hledger commands on $TIMELOG
Add hledger options to customise reports.
Justfile
https://github.com/casey/just is a nice tool for organising financial reports and scripts,
similar to make, but more robust for this use case. I can recommend it.
See also hledger and just.
Here is a Justfile
reimplementing the ft and tt scripts more simply:
$ brew install just # eg
$ alias j=just
$ cd ~/finance
$ cp ~/src/hledger/bin/Justfile . # or start from scratch: just --init
$ j
Justfile commands:
watch CMD # rerun the given command with watchexec whenever local files change
get-csv # download auto-downloadable CSVs (paypal)
import-dry # import new downloaded transactions to the main journal, dry run
import # import new downloaded transactions to the journal, logging and not printing errors
get-prices *PRICEHISTFETCHOPTS # show prices for main commodities (default: today's)
bs *HLEDGERARGS # show balance sheet
is *HLEDGERARGS # show income statement
a *HLEDGERARGS # show assets
r *HLEDGERARGS # show revenues
x *HLEDGERARGS # show expenses
ab *HLEDGERARGS # show assets bar chart
rb *HLEDGERARGS # show revenues bar chart
xb *HLEDGERARGS # show expenses bar chart
al *HLEDGERARGS # show assets line chart
rl *HLEDGERARGS # show revenues line chart
xl *HLEDGERARGS # show expenses line chart
forecast *HLEDGERARGS # print transactions predicted by forecast rules from last week on
household *HLEDGERARGS # show a draft month-end household adjustment transaction for last month
consulting *HLEDGERARGS # show consulting revenue
tdash *HLEDGERARGS # show time dashboard, redisplaying when timelog files change
tstatus *HLEDGERARGS # show current time status
twhat *HLEDGERARGS # what happened ? Show largest time balances first, today and depth 1 by default
tdots N # print line of N dots, grouped in 4s (suitable for timedot)
tx *HLEDGERARGS # horizontal time summary this year, monthly by default
ty *HLEDGERARGS # vertical time summary this year, monthly by default
tweeks *HLEDGERARGS # this and last week's time budgets
tweekspast *HLEDGERARGS # recent past weeks' time budgets
thours *HLEDGERARGS # show a bar chart of daily hours
taccunused *HLEDGERARGS # show unused / undeclared time accounts
taccunusedcat *HLEDGERARGS # show unused / undeclared time accounts by category
taccadd *HLEDGERARGS # add declarations for all undeclared time accounts
tbudgets *HLEDGERARGS # show monthly time budget performance this year
tbudgetsy *HLEDGERARGS # show monthly time budget performance this year, vertically
tbudgetsw *HLEDGERARGS # show weekly time budget performance this year
tbudgetswx *HLEDGERARGS # show weekly time budget performance this year, horizontally
watchaccounts
watchaccounts
shows hledger account names, updating on file change under the current directory.
Arguments are passed to the hledger accounts command. Useful when cleaning up accounts.
$ watchaccounts expenses -2
$ watchaccounts -f time.journal client1 date:thismonth -l
sortandmergepostings
sortandmergepostings
is an adventuresome AWK script intended to clean up and merge similar postings in a transaction
(see original discussion).
It sorts postings so that positive ones are first, negative ones last.
Within each sign, postings are sorted by commodity.
Within each commodity group, postings are sorted by amount.
Among identical amounts in the same group, postings are sorted alphabetically by account name.
Once sorted, if there are multiple postings to the same account in the same direction with the same commodity and comments, it tries to merge them (by leaving some amounts blank).
Subsequently piping the output to hledger print can recalculate the missing amounts.
Multiple runs might be needed to clean up all duplicates.
$ sortandmergepostings input.journal | hledger -f - print -x
hledger-simplebal
hledger-simplebal
shows how to reliably report a single machine-readable number with hledger.
This and the other “hledger-” scripts are add-on commands.
$ hledger simplebal
hledger-bar
hledger-bar
prints quick bar charts in the terminal.
$ hledger bar reimbursement
2023-01 ++++++
2023-02 ++
2023-03 ++
2023-04 -------
$ hledger bar # show help
$ hledger bar food # monthly food expenses
$ hledger bar 1 --count food # monthly food posting counts
$ hledger bar type:c not:tag:clopen cur:\\\\$ -W # weekly cashflow, $ only
$ hledger bar type:al not:tag:clopen cur:\\\\$ # monthly net worth change ($)
$ hledger bar type:rx --invert cur:\\\\$ # monthly profit/loss ($)
$ hledger bar -v 1 -f $TIMELOG -D # daily hours, with numbers
(or with hledger <1.50: hledger bar -- ...)
hledger-check-buynothing
hledger-check-buynothing
checks for no activity in Expense accounts on Buy Nothing Day (or other period).
hledger-git
hledger-git
provides easy version control for your journal files, using git.
Run it with no arguments for help.
$ hledger git log
$ hledger git status
$ hledger git record [MSG]
hledger-jj
hledger-jj
provides easy version control for your journal files, using jj
(and a git repo).
This is newer and better than hledger-git and hledger-pijul.
Unlike most shell scripts here, it requires ysh.
$ hledger jj log
$ hledger jj status
$ hledger jj diff
$ hledger jj commit [MSG]
hledger-pijul
hledger-pijul
provides easy version control for your journal files, using the pijul version control system.
$ hledger pijul log
$ hledger pijul status
$ hledger pijul record [MSG]
hledger-dc
hledger-dc
reads journal files which use a Dr/Cr notation instead of/in addition to amount signs:
2025-01-01 salary
Cr revenues 800 USD
Dr assets
hledger-edit
The hledger-utils python package provides
a hledger-edit command to edit the queried transactions in your $EDITOR no matter what file they reside in.
Install or upgrade:
$ pip install -U hledger-utils # might be slightly different on your system
Examples:
# Opens your $EDITOR or $VISUAL with only costs in Florida
# (if you named and tagged them like that)
# edit the transactions, save and exit your editor,
# then the changes are distributed to the original files
$ hledger edit Cost tag:location=Florida
# Automate changes by setting `$EDITOR` to a script
# (here all food we had on that one day in Florida was Fast Food 🌭 and we initially forgot to write that)
EDITOR='perl -pi -e "s|Cost:Food|Cost:Food:Fast Food|g"' hledger edit tag:location=Florida date:2022-12-20
hledger-plot
The hledger-utils python package provides
a hledger-plot command for generating charts with matplotlib.
Install or upgrade:
$ pip install -U hledger-utils # might be slightly different on your system
Examples:
$ hledger-plot -h
$ hledger plot bal -DH ^Assets -2
(or with hledger <1.50: hledger plot -- ...)
hledger-lots
hledger-lots
shows a lots report, or generates a lot sale transaction, using FIFO strategy
(and without needing subaccounts for lots).
Install or upgrade:
$ pip install -U hledger-lots
Examples:
$ hledger lots
$ hledger lots view
$ hledger lots list
hledger-report1.sh
hledger-report1.sh is a custom compound report done in shell. See also hledger-report1.hs.
hledger-timedothm
hledger-timedothm
reads timedot files which support HOURS:MINUTES notation:
2025-09-27
time ..
time 0.5
time 30m
time 0:30 ; new H:M syntax
and it displays amounts in that notation:
$ hledger timedothm sample.timedothm reg -w80
2025-09-27 (time) 0:30 0:30
(time) 0:30 1:00
(time) 0:30 1:30
(time) 0:30 2:00
hledgerj1
hledgerj1
is a small example of a wrapper script that preprocesses the journal,
converting it from a custom format, in this case a one-line journal format
where slash represents newline. Eg:
2025-01-04 shopping / assets:bank:checking / expenses:food 200 USD / expenses:supplies 50 USD
$ hledgerj1 a.j1 print
2025-01-04 shopping
assets:bank:checking
expenses:food 200 USD
expenses:supplies 50 USD
hledger haskell scripts
These scripts are written in Haskell and use hledger’s haskell API (by importing the hledger or hledger-lib haskell libraries).
They are often stack scripts.
They can do anything hledger’s builtin commands can do, and are usually more robust than command line scripts.
Some builtin commands were first developed as standalone haskell scripts.
hledger-script-example
hledger-script-example.hs
is a template for writing your own hledger-integrated add-on command.
It has the same structure as most of the add-ons here:
- a stack script for robustness
- providing command line help
- accepting common hledger options
hledger-swap-dates
hledger-swap-dates.hs
prints transactions with their date and date2 fields swapped.
hledger-check-tagfiles
hledger-check-tagfiles.hs
interprets all tag values containing a / (forward slash) as file paths, and checks that those files exist.
hledger-check-tagfiles.cabal.hs
is the same command implemented as a cabal script rather than a stack script.
hledger-register-max
hledger-register-max.hs
runs a register report and prints the posting with largest historical balance.
$ hledger-register-max -f examples/bcexample.hledger checking
2013-01-03 Hoogle | Payroll Assets:US:BofA:Checking 1350.60 USD 8799.22 USD
$ hledger register-max -f examples/bcexample.hledger checking
2013-01-03 Hoogle | Payroll Assets:US:BofA:Checking 1350.60 USD 8799.22 USD
hledger-check-postable
hledger-check-postable.hs
check that no postings are made to accounts declared with a postable:n or postable:no tag.
This can be used as a workaround when you must declare a parent account to control display order,
but you don’t want to allow postings to it. Eg, to allow postings to assets:cash but not assets
(remember that account tags are inherited):
account assets ; postable:n
account assets:cash ; postable:
hledger-check-fancyassertions
hledger-check-fancyassertions.hs
checks account balances over time in more complex ways than hledger’s built-in balance assertions.
hledger-combine-balances
hledger-combine-balances.hs
shows balance reports for two different periods side by side.
hledger-balance-as-budget
hledger-balance-as-budget.hs
uses one balance report to set budget goals for another balance report.
This is useful for compare-and-contrast reports. You can, for example, use a previous year as a budget for this year, and see how this year spending compares to your past spending.
hledger-balance-as-budget-multi
hledger-balance-as-budget.hs
uses one balance report to set budget goals for another balance report, and allows you to run multiple balance commands on them.
Like hledger run, this will load journals only once, and will be significantly faster than calling hledger-balance-as-budget
multiple times.
hledger-smooth
hledger-smooth.hs
is an incomplete attempt at automatically splitting infrequent/irregular transactions.
hledger-move
hledger-move.hs
helps make subaccount/cost-preserving transfers.
hledger-report1.hs
hledger-report1.hs is a custom compound report done in haskell. See also hledger-report1.sh.
hledger-txnsbycat.hs
hledger-txnsbycat.hs is a mixture of a balance report and a register report; it shows each account’s transactions under the account’s balance.
Add-ons
These are some official and third-party add-ons you can install as compiled programs:
hledger-ui
hledger-ui is hledger’s official terminal UI. It allows faster browsing of your accounts and transactions.
hledger-web
hledger-web is hledger’s official web UI. It allows data entry and simple reports in a web browser. It’s good for non-command-line users.
hledger-iadd
hledger-iadd is a popular alternative to the builtin add command.
hledger-interest
hledger-interest generates interest transactions.
Notes (as of 1.6.7):
- Use the https://github.com/peti/hledger-interest#readme, it’s better than the command line help or hackage description.
- hledger-interest silently ignores $LEDGER_FILE, so be sure to provide a file name explicitly with -f.
hledger-sankeymatic
hledger-sankeymatic helps export hledger data to make flow diagrams at https://sankeymatic.com. See also Charts.
Other
- hledger-stockquotes fetches market prices. Not widely used, use pricehist instead.
- hledger-diff compares two journal files. It’s now built in to hledger, so you don’t need it.
How to…
Install scripts
To use these bin scripts you must ensure they are in your $PATH and runnable:
- Shell scripts: you may need bash, or to adapt the scripts for your shell.
- Python scripts: you’ll need python 3 and pip.
- Haskell scripts: you’ll need stack. Or if you know how, you can make them cabal scripts, or install their dependencies manually and use runghc/ghc.
Here’s a suggested install procedure:
# Go to wherever you keep financial files:
$ cd ~/finance
# Get the hledger repo
# the fast way, without version control:
$ curl -LOJ https://github.com/simonmichael/hledger/archive/refs/heads/master.zip && unzip hledger-master.zip && mv hledger-master hledger
# or the slow way, with version control for easy diffing/updating/contributing
# git clone https://github.com/simonmichael/hledger
# Make a more convenient symlink to the bin directory:
$ ln -s hledger/bin
# Add the bin directory to your PATH. Eg as a bash user:
$ echo "export PATH=$PATH:$PWD/bin" >>~/.bash_profile"
$ export PATH=$PATH:$PWD/bin
# Optionally, compile all haskell scripts for faster startup:
$ cd hledger; bin/compile.sh
# Optionally, install the python scripts:
$ pip install -U hledger-utils
$ pip install -U hledger-lots
# Check that hledger's command list now includes the bin scripts.
# Eg "check-fancyassertions" and "swap-dates" should be listed:
$ hledger
Create a new script
To create a new hledger-integrated script, copy hledger-script-example.hs. On unix, the new script should be marked executable. This should do it:
$ cd bin
$ cp hledger-script-example.hs hledger-cmd.hs # replace cmd with your command name
# edit hledger-cmd.hs, updating at least the command name and help
$ stack install safe text # ensure the script's dependencies are installed
$ hledger-cmd.hs --help
cmd [OPTIONS]
My new cmd command.
...
$ stack ghc hledger-cmd.hs # optionally compile for faster startup/durability
$ hledger cmd --help
cmd [OPTIONS]
My new cmd command.
...
Run ghcid on a script
$ stack exec --package 'safe text' -- ghcid hledger-cmd.hs
...
Ok, one module loaded.
All good (1 module, at 10:50:48)
Run ghci on a script
$ stack ghci --package 'safe text' hledger-cmd.hs
...
Ok, one module loaded.
...
ghci>
Learn more about scripting hledger
See Scripting hledger.
Sponsor hledger
Building and supporting good software and documentation is very costly; hledger comes from thousands of skilled person-hours, sustained over 18+ years. Your support is invaluable and greatly appreciated! Is it the right time for you to help ? Consider:
- Has this project been helpful to you or your organisation ?
- Have you achieved enough financial success to be able to donate a little (or a lot) ?
- Would you like to cultivate the psychological and spiritual benefits of giving back / paying forward ?
- Would you like hledger to be around for a long time ? To remain actively supported ? To improve faster ?
- Would you like to support our core mission ? Which is:
To help more people achieve financial literacy, discipline and freedom, and to help grow a shared global culture of accountability and sustainability.
(See also: FAQ)
See also: FINANCE overview and reports for the project.
How to sponsor
It’s easy, even if not yet as efficient as we’d like. When choosing a donation method, you may want to check the fees (below). The hledger project is not a registered charity, so your donations may not be tax-deductible. The CFO (Simon) pays US and state income tax on all donations in addition to the fees.
Sponsor the lead developer
These donations help support Simon, allowing me to do more hledger work. Thank you! You can also offer me bounties or consulting gigs.
Sponsor other developers
You can find their names at CREDITS, check their website or Github profile, and offer donations, bounties or paid work.
Sponsor the hledger project
These donations are public and managed by the hledger CFO (Simon), with input from the hledger team and community. So far, project funds have been used for project expenses, regression bounties, and one social good donation.
Sponsor specific tasks
Post a bounty pledge on an open issue. When resolved, pay the claimant directly (honour system).
Make a one-time donation via Open Collective, describing the bounty. When resolved, the claimant submits a payment request for approval by the CFO (see our regression bounty process).
Fees
Donation platforms:
Github
- If donating from a personal account
- Charges no fees.
- If donating from an organization account
- Charges 3% platform fee.
- Charges 3% payment processing fee if paying via credit card.
Liberapay
- Charges no platform fee, other than voluntary donations to their project (they are non-profit).
- Charges payment processor fees (Paypal or Stripe, see below).
Open Collective
- Open Source Collective (fiscal host) charges 10% of contributions (and 15% of this goes to Open Collective).
- Open Collective may solicit a voluntary tip from contributions.
- Open Collective charges payment processor fees on contributions if paying with PayPal, Stripe, or Wise, see below.
- Expense payouts have no fees.
Payment processors:
Paypal
- 0% to 3.5% + some fixed amount, depending on whether you’re paying in the same country, there’s a currency conversion, etc. Liberapay says: “the average fee percentages have been 3.1% for the payments processed by Stripe and 5% for the payments processed by PayPal.”
Stripe
- 2.9% + 0.30 USD.
Wise
- Unclear.
Our fiscal host on Open Collective currently is Open Source Collective, “a non-profit umbrella organisation providing financial and legal infrastructure for thousands of open source projects”. So their fee is at least a sort of donation to support other free/open-source software.
Sponsors
Thank you to our generous sponsors for their support. They include:
Contributor Quick Start
New contributors of all kinds are always welcome in the hledger project. We invite you to jump in, and thank you!
There are many ways to help. Browse the ideas below, and/or say hello in the chat and we’ll help find you a useful job.
Visitor / passer-by ?
- Give feedback on the site and your impressions of the project, small or large, good or bad. This is valuable.
New user ?
- Report your new user experiences, small or large, good or bad. This is valuable.
Tech supporter ?
- Share what you’ve learned so far to help others. This is a quadruple win - it helps them, improves your own understanding, builds community, and frees up maintainer time!
- Add translation to your language. Starting with the tldr has high value. Or if you want to spend minimal effort, then just translate the top level account names
Funder ?
- Become a financial backer: Sponsor hledger
- Contribute or pledge bounties on issues you care about
- Ask your organization to contribute
- Work on project finance - accounting, fundraising, sustainability..
Tester ?
- Test installation on platforms you have access to
- Test examples, advice, and links in the docs
- Run the latest release or developer build in daily use
- Run tests
- Run benchmarks
- Report packaging, documentation, UX, functional, performance issues (https://bugs.hledger.org/new)
- Test new releases, report regressions and collect regression finder bounties
- Discuss and help analyse problems via chat/mail list/issue tracker
Bug wrangler ?
- Respond to issue reports when needed, especially if they are from new reporters
- Add appropriate labels to issues to categorise them
- Follow up on inactive/stalled issues, help move things forward
- Link/merge related issues
- Keep an overview of the issue tracker, report stats & trends
- Improve issues urls & dashboard(s)
- Help ensure a consistently good bug-reporting and PR-contributing experience
Bug fixer ?
- Get familiar with issue tracker, issue labels, shortcut urls, issue dashboards..
- Review open bug reports
- Try to fix or help fix some
- Fix regressions and collect regression fixer bounties
Developer ?
- Give feedback on your experience using the hledger packages
- Suggest API improvements
- See the Developer FAQ and other Developer docs.
Technical writer ?
- Get familiar with the documentation, website and online presence; review and test
- Get familiar with the doc/site source files and generation process (see Just, Make, Shake)
- Help improve user, contributor, process docs, translation
Web designer / webmaster ?
- Review and help improve our web presence
Graphic designer ?
- Review and improve logos, graphics, design language
- Contribute illustrations, diagrams, cartoons, mockups
Packager ?
- Start/test/improve hledger’s packaging on various platforms
- Find/assist/take over from existing packagers
- Improve packaging-related docs/links
- Develop mac or windows installers
Marketer / communicator ?
- Clarify project goals, value proposition, brand, mission, story
- Monitor product-market fit
- Identify new opportunities
- Influence developer priorities
- Spread the word!
Product designer ?
- Contribute design input to discussions in issue tracker and elsewhere
- Develop your whole-system view of the hledger “product” (user software, docs, online presence, new user experience etc.)
Community builder/moderator ?
- Participate in support channels
- As a regular member or moderator, help to resolve/report incidents
- Help uphold and improve our community structures and dynamics
Project manager ?
- Monitor, report on project progress and performance
- Research, compare and report on successful projects, related projects
- Identify collaboration opportunities
- Assist with marketing, communication, outreach
- Assist with maintainer tasks
Maintainer / co-maintainer ?
- Manage and ship releases
- Manage the project roadmap
- Clarify/update goals and principles
- Resolve issues
- Serve the community
CREDITS
hledger is brought to you by…
the Issue wranglers,
Bug hunters,
Design dreamers,
Code slingers,
Doc poets,
Package marshals,
Helping hands,
Good news preachers,
Bank rollers,
Broom pushers,
the pioneer John Wiegley,
and innumerable other benefactors.
Commit authors
https://github.com/simonmichael/hledger/graphs/contributors
11231 commits in 16 years by 155 people as of 2022-12-21:
| Commits | Author | Notes (chat me with updates!) |
|---|---|---|
| 9749 | Simon Michael | founder, project leader, lead developer |
| 460 | Stephen Morgan | performance, code cleanup, runtime error removal, cli/ui output, periodic transactions, parsing, deps, valuation, –gain report, hlint, lensification |
| 161 | Dmitry Astapov | roi, files commands; –transpose; merge/improve –budget; generalise –forecast/–auto; docker packaging; improved CSV parsing, balancing, periodic transactions, close, parsing, docs, tests |
| 81 | Vladimir Zhelezov | new bash shell completions |
| 72 | Alex Chen | parsing improvements, code cleanups, better error messages; dep updates |
| 52 | Mykola Orliuk | hledger-budget, hledger-prices add-ons; scientific number notation; print, hledger-equity, hledger-rewrite, –pivot, space, parsing improvements; code updates; GHC 8.0 support |
| 51 | Jakob Schöttl | bash completions; register –invert; timeclock parsing improvements; code cleanups |
| 40 | Everett Hildenbrandt | doc toolchain updates, switch from hakyll to pandoc; csv parser improvement |
| 31 | Jakub Zárybnický | hledger-web, hledger-ui improvements |
| 29 | Marko Kocić | build, hlint fixes; hledger-web improvement |
| 26 | Justin Le | bs/cf/is improvements |
| 26 | Dominik Süß | hledger-web layout improvements |
| 24 | Thomas R. Koll | hledger-web improvements |
| 17 | Peter Simons | build, dep fixes |
| 17 | Joseph Weston | parsing improvements; test, dep updates |
| 16 | Aleksandar Dimitrov | CSV separator rule |
| 15 | Nolan Darilek | Sandstorm app |
| 14 | Brian Wignall | output dates in YYYY-MM-DD format |
| 12 | Tim Docker | parsing improvements; proper date support; P directive (first code contributor, 2008-11 |
| 12 | Lawrence | –commodity-column; multi-day-of-week period expressions |
| 11 | Trygve Laugstøl | hledger-web improvements; –format; CSV –rules-file, stdin support, separate in/out fields, interpolated description; test updates |
| 11 | Jesse Rosenthal | parsing improvementss |
| 10 | Nick Ingolia | include directive, account directive; parsing improvements (second code contributor, 2008-12 |
| 10 | Caleb Maclennan | payees/notes/descriptions commands; print improvement; doc, code, test updates |
| 10 | Julien Moutinho | print CSV output; hledger-equity, hledger-rewrite, hledger-web, parsing improvements; hledger-check-dates addon |
| 10 | Henning Thielemann | hledger-web improvements, quarter periods, code cleanup |
| 9 | Ryan Desfosses | warnings, docs, hledger-web fixes |
| 9 | Eli Flanagan | hledger-web date picker; build, doc updates |
| 8 | Imuli | support multiple input files |
| 8 | Felix Yan | dep updates; doc updates |
| 8 | Nicholas Niro | balancesheetequity command; balance tests, fix |
| 8 | Moritz Kiefer | –pretty-tables; dep updates; switch to megaparsec; cabal.project file; space leak fixes |
| 7 | Jacob Weisz | Sandstorm app improvements |
| 7 | Hans-Peter Deifel | dep updates; csv rules parsing fix; command line parsing improvements |
| 6 | Clint Adams | CSV account2 setting; dep updates; cabal test suites |
| 6 | Roman Cheplyaka | chart command; hledger add improvements; –no-new-accounts |
| 6 | Gaith Hallak | undo (<) in hledger add; command line parsing improvement |
| 6 | Samuel May | multicommodity balance assertions |
| 5 | Martin Michlmayr | doc updates |
| 5 | Jacek Generowicz | command line parsing fix |
| 5 | Eric Mertens | –pretty-tables improvements; CSV whitespace fix |
| 5 | Xinruo Sun | hledger-web –static-root; hledger-web autocomplete improvements; hledger print tag filtering |
| 4 | Damien Cassou | payee directive, check improvements, info manual directory entries |
| 4 | Michael Snoyman | hledger-web improvements |
| 4 | Sergey Astanin | unicode support |
| 3 | Michael Sanders | & (AND) operator in CSV if rules |
| 3 | Christian G. Warden | cashflow tweaks; hledger-rewrite (auto postings) commodity substitution |
| 3 | Eric Kow | hledger add improvements |
| 3 | toonn | PR template improvements |
| 3 | Carlos Lopez-Camey | hledger-web add form improvements |
| 3 | Johannes Gerer | balance assignments; generalise parser types |
| 3 | Malte Brandy | –pivot, newline in CSV rule-generated comments |
| 2 | Pavlo Kerestey | quoting fixes |
| 2 | Alejandro García Montoro | hledger-web –cors |
| 2 | Gergely Risko | comment directive |
| 2 | aragaer | fix commodity checking fix; fix –drop with csv |
| 2 | Stefano Rodighiero | hledger-dupes addon |
| 2 | Arsen Arsenović | hledger-web XSS fix |
| 2 | crocket | improve hledger-ui editor support |
| 2 | Arnout Engelen | hledger-web register chart improvements |
| 2 | gwern | whitespace, Haskell98 cleanups |
| 2 | Arjen Langebaerd | -c/–commodity-style option |
| 2 | jungle-boogie | tutorial updates |
| 2 | Max Bolingbroke | unicode-aware regexes; csv date-format rule |
| 2 | Matthias Kauer | investment doc improvements |
| 2 | Elijah Caine | git/nix tweaks |
| 2 | Judah Jacobson | readline editing, tab completion in hledger add |
| 2 | Ben Creasy | doc updates |
| 2 | Christoph Nicolai | doc updates |
| 2 | Sergei Trofimovich | bounds, build updates |
| 2 | Sam Jeeves | balance assertion line number reporting |
| 2 | Alex Hirzel | doc updates |
| 2 | Pranesh Prakash | doc updates |
| 1 | flip111 | |
| 1 | jeevcat | |
| 1 | legrostdg | |
| 1 | max thomas | |
| 1 | trevorriles | |
| 1 | zieone | |
| 1 | Aerex | |
| 1 | ˌbodʲɪˈɡrʲim | |
| 1 | Aiken Cairncross | |
| 1 | Alan Young | |
| 1 | Alvaro Fernando García | |
| 1 | Amarandus | |
| 1 | Amitai Burstein | |
| 1 | Andreas Pauley | |
| 1 | Andrew Jones | |
| 1 | Andriy Mykhaylyk | |
| 1 | Ben Boeckel | |
| 1 | Boyd Kelly | |
| 1 | Brian Scott | |
| 1 | Bryan Richter | |
| 1 | Carel Fellinger | |
| 1 | Carl Richard Theodor Schneider | |
| 1 | Charlotte Van Petegem | |
| 1 | Colin Woodbury | |
| 1 | Daniel Gröber | |
| 1 | David D Lowe | |
| 1 | David Reaver | |
| 1 | David Zhang | |
| 1 | Doug Goldstein | |
| 1 | Evilham | |
| 1 | Felix Van der Jeugt | |
| 1 | Fun Ilrys (Nissar Chababy) | |
| 1 | Gabriel Ebner | |
| 1 | Garret McGraw | |
| 1 | Jan Zerebecki | |
| 1 | Jeff Richards | |
| 1 | Joachim Breitner | |
| 1 | Joaquin “Florius” Azcarate | |
| 1 | Joe Horsnell | |
| 1 | Johann Klähn | |
| 1 | John Wiegley | |
| 1 | Joshua Chia | |
| 1 | Joshua Kehn | |
| 1 | Kyle Marek-Spartz | |
| 1 | Lawrence Wu | |
| 1 | Luca Molteni | |
| 1 | Léo Gaspard | |
| 1 | M Parker | |
| 1 | Mark Hansen | |
| 1 | Mateus Furquim | |
| 1 | Michael Kainer | |
| 1 | Michael Walker | |
| 1 | Mick Dekkers | |
| 1 | Mitchell Rosen | |
| 1 | Nadrieril | |
| 1 | Nicolas Wavrant | |
| 1 | Nikhil Jha | |
| 1 | Nissar Chababy | |
| 1 | Oliver Braun | |
| 1 | Omari Norman | |
| 1 | Patrick Fiaux | |
| 1 | Patrik Keller | |
| 1 | Pavan Rikhi | |
| 1 | Pia Mancini | |
| 1 | Rick Lupton | |
| 1 | Rui Chen | |
| 1 | Sam Doshi | |
| 1 | ShrykeWindgrace | |
| 1 | Shubham Lagwankar | |
| 1 | Simon Hengel | |
| 1 | SpicyCat | |
| 1 | Steven R. Baker | |
| 1 | TANIGUCHI Kohei | |
| 1 | Timofey ZAKREVSKIY | |
| 1 | Vladimir Sorokin | |
| 1 | Wad | |
| 1 | afarrow | |
| 1 | agander | |
| 1 | awjchen | |
| 1 | azure-pipelines[bot] | |
| 1 | charukiewicz |
hledger by example
A small but growing book introducing (some of) hledger, one step at a time.
You can use the sidebar links, the left and right arrow buttons on this page, or the left and right arrow keys, to step through the pages. Eg press the right arrow key now to step to the next page.
- Get hledger installed
- Check your setup
- Start a journal
- Add a transaction: starting balances
- Journal entries
- Account names
- More transactions
- Overview of commands
- print: show whole transactions
- register: show detailed changes
- balance: show account totals
- balancesheet: assets and liabilities
- incomestatement: revenues and expenses
- Reading CSV files
- Importing new transactions
- Reading timeclock files
- Reading timedot files
Get hledger installed
Each new hledger version is better than the preceding ones, so using the latest is ideal. And you’ll want to have it installed somewhere in your system’s PATH, so that you can just type “hledger” and it runs.
Install shows all the ways to get hledger installed on your computer. But here’s a quick way: install the official release binaries, by copy/pasting the appropriate command below into a terminal window.
On a Linux machine with an intel processor:
curl -sL https://github.com/simonmichael/hledger/releases/download/1.50/hledger-linux-x64.tar.gz | tar -xzv -f- -C/usr/local/bin hledger hledger-ui hledger-web
On a modern mac with an ARM (“M”) processor:
curl -sL https://github.com/simonmichael/hledger/releases/download/1.50/hledger-mac-arm64.tar.gz | tar -xzv -f- -C/usr/local/bin hledger hledger-ui hledger-web
On an older mac with an intel processor:
curl -sL https://github.com/simonmichael/hledger/releases/download/1.50/hledger-mac-x64.tar.gz | tar -xzv -f- -C/usr/local/bin hledger hledger-ui hledger-web
On a Windows machine with an intel or ARM processor:
(To get a terminal window, type: Windows-R, powershell, Return)
cd ~
curl https://github.com/simonmichael/hledger/releases/download/1.50/hledger-windows-x64.zip -OutFile hledger-windows-x64.zip
Expand-Archive hledger-windows-x64.zip -Force -DestinationPath AppData\Roaming\local\bin
The end result should be that you can run hledger --version and see a recent version:
$ hledger --version
hledger 1.50 ...
The examples in this book assume you are using this latest hledger version. (And if you’re on Windows, that you are using powershell, not CMD.EXE.)
Press the right arrow key again (or click the right arrow button) to step to the next page.
(Part of hledger by example.)
Check your setup
You can run hledger’s setup command at any time to check for configuration problems.
Eg on Windows, at this stage, it may be showing something like this:

You don’t need to worry overmuch about what it says, but anything in red might need attention. (We’ll make this less color-dependent in future.)
(Part of hledger by example.)
Start a journal
hledger reads transactions from, and appends new transactions to, a journal file.
This is named after the General Journal in bookkeeping.
You can specify it with -f FILE in hledger commands.
But it’s good to configure a default file, so that you don’t have to write -f FILE every time.
hledger setup or hledger files will show if a default file is configured:
PS C:\Users\Simon> hledger files
hledger.exe: Error: data file "C:\Users\Simon\.hledger.journal" was not found.
Please create it first, eg with "hledger add" or a text editor.
Or, specify an existing data file with -f or $LEDGER_FILE.
If you know how to configure its path in the LEDGER_FILE environment variable,
you can keep this file wherever you like.
For example, you might have LEDGER_FILE=~/finance/main.journal,
where ~/finance is a version-controlled directory.
If not, just use the default location: .hledger.journal in your home directory.
You can move the file later if needed.
Start the journal
You can start the journal by running
hledger add
and adding a transaction (see the next page for an example).
(If you’re using hledger 1.50.3-1.51.1, hledger add -f ~/.hledger.journal is needed.)
Start the journal, another way
Or if you prefer, you can create the file yourself with your favourite text editor.
On unix systems, eg:
emacs ~/.hledger.journal
or:
touch ~/.hledger.journal
Files containing non-ascii characters should use the system’s text encoding. (See Install: Text encoding.)
On Windows:
notepad $HOME/.hledger.journal
or:
mv -ErrorAction SilentlyContinue ~/.hledger.journal ~/.hledger.journal.old
Set-Content -Path $HOME/.hledger.journal -Value ""
These Windows commands ensure the file uses system’s text encoding.
You can see what your system text encoding is by running [System.Text.Encoding]::Default.EncodingName or hledger setup.
If you need your files to be compatible with non-Windows machines, the “Unicode (UTF-8)” encoding is best. For that, you might need to set “Language for non-Unicode programs > Use Unicode UTF-8 for worldwide language support”.
(Part of hledger by example.)
Add a transaction: starting balances
The first transaction in a journal is usually a “starting balances” transaction, which sets up the starting balances in one or more asset and liability accounts. (Assets are things you own, like a bank checking account or the cash in your wallet. Liabilities are things you owe, like a credit card balance.)
Let’s start by recording one starting balance: how much cash is in your wallet today.
You can do it quickly with a text editor, or with more guidance using hledger’s add command. We’ll show both. (hledger-web is another way.)
Using a text editor
If you’re comfortable using text editors, open the journal file in your favourite editor.
(If you forgot where the journal file is, run hledger files.)
Then add an entry something like the below. Use today’s date and the actual currency and amount.
Feel free to adapt the account names and description, too (more on account names later):
2025-07-01 starting balances
assets:cash $10
equity:start $-10
This means “on july 1st 2025, 10 dollars was transferred from equity:start to assets:cash”. This is how bookkeepers say “we’ll start tracking the assets:cash balance from july 1st onward”. The next section steps through this entry in more detail.
Using hledger add
Or, you can create the same entry using hledger’s easy add command.
add will prompt you for the information needed.
And it provides useful defaults and completions (especially later when you have more data).
Run it like so (if you’re on Windows, also write --color=no):
PS C:\Users\Simon> hledger add --color=no
It first prints some help (which is slightly wrong for Windows: the TAB key won’t work there):
Adding transactions to journal file C:\Users\Simon\.hledger.journal
Any command line arguments will be used as defaults.
Use tab key to complete, readline keys to edit, enter to accept defaults.
An optional (CODE) may follow transaction dates.
An optional ; COMMENT may follow descriptions or amounts.
If you make a mistake, enter < at any prompt to go one step backward.
To end a transaction, enter . when prompted.
To quit, enter . at a date prompt or press control-d or control-c.
Date [2025-07-01]:
Then it prompts you for the transaction’s date. Press RETURN to accept today’s date.
Description: starting balances
Type a description, such as “starting balances”, and press RETURN.
Account 1: assets:cash
Type an account name. I am calling the wallet “assets:cash”. You can use any account names that make sense to you, in your preferred language. But, more about account names later.
Amount 1: $10
Instead of “$10”, enter the amount currently in your wallet, and whatever currency symbol or currency code is appropriate. If you have multiple currencies, just record the main one for now.
Account 2: equity:start
Since $10 has appeared in assets:cash, we must say where it came from. Starting balances always come from an equity account. (Equity represents “ownership” and other things; if you’re doing personal accounting, this might be the only time you’ll use it.)
Amount 2 [$-10]:
Transactions move an amount between accounts, with none of it “left over”. The assets:cash account was increased by $10, so some other account must be decreased by the same amount. hledger suggests this balancing amount as the default input: $-10. Press RETURN to accept it.
Account 3 (or . or enter to finish this transaction):
Transactions can involve any number of accounts, though two is the most common. Press RETURN to complete this transaction.
2025-07-01 starting balances
assets:cash $10
equity:start $-10
Save this transaction to the journal ? [y]:
hledger shows a preview of the journal entry that will be appended to the journal file. Press RETURN to accept it.
Saved.
Starting the next transaction (. or ctrl-D/ctrl-C to quit)
Date [2025-07-01]: .
PS C:\Users\Simon>
hledger starts to read another transaction.
Press . and RETURN to end the add session and exit to the command line.
You have recorded your first transaction!
(Part of hledger by example.)
Journal entries
Here’s the journal entry for a common type of transaction - a purchase - with the parts labelled in red. You can read more about each part in the manual if needed.:
This entry means: “on april 10th 2018, there was a $9.19 car-related expense, paid with the Acme credit card”.
A few more notes:
-
To reduce errors, we use Double Entry Bookkeeping. This means we always record both the source and the destination of money - where it came from and where it went.
-
Expenses:Automotive, a spending category, is just another kind of account. It’s not a physical account like your wallet (AKA assets:cash), or a bank account (like Liabilities:Acme Credit Card); it’s an expense account.
-
We use a convenient sign notation: money leaving an account (a Credit, in bookkeeping language) is written as a negative number, and money entering an account (a Debit) is written as a positive number.
-
Because of 1 and 3, the amounts in a transaction’s journal entry must always add up to zero. This helps prevent errors and guides us toward writing correct entries.
-
Why is there no amount next to Liabilities:Acme Credit Card ? As a convenience, we are allowed to leave one amount blank, and it will be inferred automatically (eg $-9.19 is inferred above). This is optional; it saves typing.
(Part of hledger by example.)
Account names
Account names can be whatever you like. They can be capitalised or uncapitalised:
assets:cash
Assets:Cash
They can be in any language:
actifs:espèces
activos:caja
资产:现金
資産:現金
자산:현금
They can contain spaces (this is the reason that two or more spaces are sometimes required to end account names, in journal entries):
liabilities:credit card
Assets:Bank of the West
Spaces are good for readability, but can make command lines and scripting a little harder. You can also use punctuation, or run words together:
Assets:Bank-of-the-West
Assets:Bank_of_the_West
Assets:BankOfTheWest
assets:bankofthewest
The best account names are easy to remember, easy to spell, and easy to type.
It can be helpful if the last part of account names is unique. Eg say you have business and personal checking accounts:
assets:wells fargo:checking
assets:credit union:checking
you could instead name them something like:
assets:wells fargo:bchecking
assets:credit union:pchecking
making it easier to match the account you want (hledger register pchecking).
Account hierarchy
Colons in account names indicate subaccounts, subsubaccounts and so on, to any depth.
The account name assets:bank:checking actually represents a hierarchy of three accounts:
assets ; top-level account (depth 1)
assets:bank ; second-level account (depth 2)
assets:bank:checking ; third-level account (depth 3)
Reports usually show accounts as a flat list, as above, for simplicity. But they can also show accounts in hierarchical “tree mode”:
assets
bank
checking
Hierarchy is optional. You could use simple account names with no subaccounts:
cash
checking
credit card
starting balances
gifts received
salary
food
health
recreation
rent
But it’s common to use at least two levels of accounts, to keep them organised. Eg:
assets:bank
assets:cash
liabilities:credit card
equity:start
income:gifts
income:salary
expenses:food
expenses:health
expenses:recreation
expenses:rent
expenses:utilities
or, displayed in tree mode:
assets
bank
cash
equity
start
expenses
food
health
recreation
rent
utilities
income
gifts
salary
liabilities
credit card
The set of account names that you use is called the Chart of Accounts. Your chart of accounts will depend on your accounting and reporting needs. Start with a simple one, and expect it to grow and evolve as your needs change.
(Part of hledger by example.)
More transactions
Now, to test some reports, let’s make your journal look like the one below.
As usual, there two common ways to update the journal:
- You could delete the old file, then use
hledger addto add each of the entries below. - Or you can open the file in your text editor and replace the contents with the below.
2025-01-01 starting balances
assets:cash 100 USD
assets:bank:checking 1000 USD
liabilities:credit card -400 USD
equity:start
2025-01-01 pay rent
assets:bank:checking
expenses:rent 800 USD
2025-01-02 salary
revenues:salary
assets:bank:checking 1000 USD
2025-01-03 pay half of credit card balance
assets:bank:checking
liabilities:credit card 200 USD
2025-01-04 shopping
assets:bank:checking
expenses:food 200 USD
expenses:supplies 50 USD
A few differences from the previous example:
- The starting balances transaction now sets balances for three accounts, not just one.
- One of them is a liability (a credit card with a balance owed); so this one is negative.
- Currency symbols are using a different style: a three-letter currency code, on the right side.
These entries represent the following money flows:
| 1. equity -> assets, liabilities | set starting balances |
| 2. checking -> rent | pay rent from checking account |
| 3. revenues -> checking | receive salary to checking account |
| 4. checking -> credit card | pay part of credit card balance from checking account |
| 5. checking -> food, supplies | a mixed purchase from checking account |
(Part of hledger by example.)
Overview of commands
To see an overview of hledger’s commands, run hledger commands or just hledger.
If you have hledger and just the hledger-ui and hledger-web addons installed,
it will look something like this:
$ hledger
__ __ __
/ / / /__ ___/ /__ ____ ____
/ _ \/ / -_) _ / _ `/ -_) __/
/_//_/_/\__/\_,_/\_, /\__/_/
/___/
-------------------------------------------------------------------------------
hledger 1.43.2-gedbf4d4f3-20250613, mac-aarch64
Usage: hledger [COMMAND] [OPTIONS] [ARGS]
Commands:
HELP (docs, demos..)
commands show the commands list (default)
demo [DEMO] show brief demos in the terminal
help [-i|-m|-p] [TOPIC] show the hledger manual with info/man/pager
--tldr [COMMAND] show command examples [for command] with tldr
--help/-h [COMMAND] show command line help [for command]
--info [COMMAND] show the hledger manual [for command] with info
--man [COMMAND] show the hledger manual [for command] with man
more help: https://hledger.org
USER INTERFACES (alternate UIs)
repl run commands from an interactive prompt
run run command scripts from files or arguments
ui run a terminal UI (hledger-ui)
web run a web UI (hledger-web)
ENTERING DATA (add or edit transactions)
add add transactions using interactive prompts
import add new transactions from other files, eg CSV files
BASIC REPORTS (simple lists)
accounts show account names
codes show transaction codes
commodities show commodity/currency symbols
descriptions show transaction descriptions
files show data files in use
notes show note part of transaction descriptions
payees show payee part of transaction descriptions
prices show historical market prices
stats show journal statistics
tags show tag names
STANDARD REPORTS (the most useful financial reports)
print show full transaction entries, or export journal data
aregister (areg) show transactions & running balance in one account
register (reg) show postings & running total in one or more accounts
balancesheet (bs) show assets and liabilities
balancesheetequity (bse) show assets, liabilities and equity
cashflow (cf) show changes in liquid assets
incomestatement (is) show revenues and expenses
ADVANCED REPORTS (more versatile/advanced reports)
balance (bal) show balance changes, end balances, gains, budgets..
roi show return on investments
CHARTS (bar charts, line graphs..)
activity show posting counts as a bar chart
GENERATING DATA (generate or download journal entries; less common)
close generate transactions to zero/restore/assert balances
rewrite add postings to transactions, like print --auto
MAINTENANCE (error checking, data management..)
check run any of hledger's built-in correctness checks
diff compare an account's transactions in two journals
setup check and show the status of the hledger installation
test run some self tests
OTHER ADDONS (more hledger-* commands found in PATH):
A few of them are commands which change data. But most are reports. They fall into these broad types:
- list reports, which list items of a particular type (accounts, commodities, payees…)
- print-like reports, which show whole transactions as journal entries (print, close…)
- register reports, which show transactions or postings compactly with a running total (register, aregister…)
- balance reports, which show total balance changes or end balances for multiple accounts (balance, balancesheet, incomestatement…)
- other kinds of report (activity, roi…)
Next, we’ll explore some reports.
(Part of hledger by example.)
print: show whole transactions
The print command shows whole transactions (journal entries), much as they appear in the journal:
$ hledger print
2025-01-01 starting balances
assets:cash 100 USD
assets:bank:checking 1000 USD
liabilities:credit card -400 USD
equity:start
2025-01-01 pay rent
assets:bank:checking
expenses:rent 800 USD
2025-01-02 salary
revenues:salary
assets:bank:checking 1000 USD
2025-01-03 pay half of credit card balance
assets:bank:checking
liabilities:credit card 200 USD
2025-01-04 shopping
assets:bank:checking
expenses:food 200 USD
expenses:supplies 50 USD
To see the missing amounts, add the -x or --explicit flag:
$ hledger print -x
2025-01-01 starting balances
assets:cash 100 USD
assets:bank:checking 1000 USD
liabilities:credit card -400 USD
equity:start -700 USD
2025-01-01 pay rent
assets:bank:checking -800 USD
expenses:rent 800 USD
2025-01-02 salary
revenues:salary -1000 USD
assets:bank:checking 1000 USD
2025-01-03 pay half of credit card balance
assets:bank:checking -200 USD
liabilities:credit card 200 USD
2025-01-04 shopping
assets:bank:checking -250 USD
expenses:food 200 USD
expenses:supplies 50 USD
You can select transactions involving certain accounts:
$ hledger print expenses:food
2025-01-04 shopping
assets:bank:checking
expenses:food 200 USD
expenses:supplies 50 USD
Or by description:
$ hledger print desc:'pay half'
2025-01-03 pay half of credit card balance
assets:bank:checking
liabilities:credit card 200 USD
Or by date. Eg, on january 1st:
$ hledger print date:2025-01-01
2025-01-01 starting balances
assets:cash 100 USD
assets:bank:checking 1000 USD
liabilities:credit card -400 USD
equity:start
2025-01-01 pay rent
assets:bank:checking
expenses:rent 800 USD
On or after january 3rd:
$ hledger print date:2025-01-03..
2025-01-03 pay half of credit card balance
assets:bank:checking
liabilities:credit card 200 USD
2025-01-04 shopping
assets:bank:checking
expenses:food 200 USD
expenses:supplies 50 USD
(Part of hledger by example.)
register: show detailed changes
register shows individual postings, and their running total:
$ hledger register
2025-01-01 starting balances assets:cash 100 USD 100 USD
assets:bank:checking 1000 USD 1100 USD
li:credit card -400 USD 700 USD
equity:start -700 USD 0
2025-01-01 pay rent assets:bank:checking -800 USD -800 USD
expenses:rent 800 USD 0
2025-01-02 salary revenues:salary -1000 USD -1000 USD
assets:bank:checking 1000 USD 0
2025-01-03 pay half of credi.. assets:bank:checking -200 USD -200 USD
li:credit card 200 USD 0
2025-01-04 shopping assets:bank:checking -250 USD -250 USD
expenses:food 200 USD -50 USD
expenses:supplies 50 USD 0
Each line is a posting, part of some transaction. There are five columns:
date description account name change amount running total
It adapts to your terminal width, so descriptions and account names may be abbreviated. As usual, a positive change amount means an increase to that account, negative means a decrease.
register is more useful when restricted to a particular account or set of accounts.
Here we can see the history of the assets:bank:checking account:
$ hledger register checking
2025-01-01 starting balances assets:bank:checking 1000 USD 1000 USD
2025-01-01 pay rent assets:bank:checking -800 USD 200 USD
2025-01-02 salary assets:bank:checking 1000 USD 1200 USD
2025-01-03 pay half of credi.. assets:bank:checking -200 USD 1000 USD
2025-01-04 shopping assets:bank:checking -250 USD 750 USD
Or the activity in expense accounts:
$ hledger register expenses
2025-01-01 pay rent expenses:rent 800 USD 800 USD
2025-01-04 shopping expenses:food 200 USD 1000 USD
expenses:supplies 50 USD 1050 USD
(Part of hledger by example.)
balance: show account totals
balance shows (by default) the total balance change per account, usually for multiple accounts at once:
$ hledger balance
750 USD assets:bank:checking
100 USD assets:cash
-700 USD equity:start
200 USD expenses:food
800 USD expenses:rent
50 USD expenses:supplies
-200 USD liabilities:credit card
-1000 USD revenues:salary
--------------------
0
The balance command is quite flexible.
The above is its original output style (as in Ledger), with amounts on the left; it is simple.
Specifying a report interval, like monthly, enables more flexible tabular output, with a title.
Eg here are the changes broken down by month:
$ hledger balance -M
Balance changes in 2025-01:
|| Jan
=========================++===========
assets:bank:checking || 750 USD
assets:cash || 100 USD
equity:start || -700 USD
expenses:food || 200 USD
expenses:rent || 800 USD
expenses:supplies || 50 USD
liabilities:credit card || -200 USD
revenues:salary || -1000 USD
-------------------------++-----------
|| 0
(You can add --pretty to your command or hledger config file for prettier borders:)
$ hledger balance -M --pretty
Balance changes in 2025-01:
┌─────────────────────────╥───────────┐
│ ║ Jan │
╞═════════════════════════╬═══════════╡
│ assets:bank:checking ║ 750 USD │
│ assets:cash ║ 100 USD │
│ equity:start ║ -700 USD │
│ expenses:food ║ 200 USD │
│ expenses:rent ║ 800 USD │
│ expenses:supplies ║ 50 USD │
│ liabilities:credit card ║ -200 USD │
│ revenues:salary ║ -1000 USD │
├─────────────────────────╫───────────┤
│ ║ 0 │
└─────────────────────────╨───────────┘
Here are the changes by day:
$ hledger balance -D
Balance changes in 2025-01-01..2025-01-04:
|| 2025-01-01 2025-01-02 2025-01-03 2025-01-04
=========================++================================================
assets:bank:checking || 200 USD 1000 USD -200 USD -250 USD
assets:cash || 100 USD 0 0 0
equity:start || -700 USD 0 0 0
expenses:food || 0 0 0 200 USD
expenses:rent || 800 USD 0 0 0
expenses:supplies || 0 0 0 50 USD
liabilities:credit card || -400 USD 0 200 USD 0
revenues:salary || 0 -1000 USD 0 0
-------------------------++------------------------------------------------
|| 0 0 0 0
-D/--daily is the smallest interval it can show.
If you need more detail than that, use a register report.
Eg, to see the detailed changes to checking on 2025-01-03:
$ hledger register checking date:2025-01-03
2025-01-03 pay half of credi.. assets:bank:checking -200 USD -200 USD
Usually we restrict balance to certain accounts:
$ hledger balance assets liabilities
750 USD assets:bank:checking
100 USD assets:cash
-200 USD liabilities:credit card
--------------------
650 USD
Here’s a daily expenses report:
$ hledger balance expenses -D
Balance changes in 2025-01-01..2025-01-04:
|| 2025-01-01 2025-01-02 2025-01-03 2025-01-04
===================++================================================
n expenses:food || 0 0 0 200 USD
expenses:rent || 800 USD 0 0 0
expenses:supplies || 0 0 0 50 USD
-------------------++------------------------------------------------
|| 800 USD 0 0 250 USD
We can add Total and Average columns:
$ hledger balance expenses -DTA
Balance changes in 2025-01-01..2025-01-04:
|| 2025-01-01 2025-01-02 2025-01-03 2025-01-04 Total Average
===================++===================================================================
expenses:food || 0 0 0 200 USD 200 USD 50 USD
expenses:rent || 800 USD 0 0 0 800 USD 200 USD
expenses:supplies || 0 0 0 50 USD 50 USD 12 USD
-------------------++-------------------------------------------------------------------
|| 800 USD 0 0 250 USD 1050 USD 262 USD
Sort by amount, and show amounts as percentages of their column total:
$ hledger balance expenses -DTAS -%
Balance changes in 2025-01-01..2025-01-04:
|| 2025-01-01 2025-01-02 2025-01-03 2025-01-04 Total Average
===================++==================================================================
expenses:rent || 100.0 % 0 0 0 76.2 % 76.2 %
expenses:food || 0 0 0 80.0 % 19.0 % 19.0 %
expenses:supplies || 0 0 0 20.0 % 4.8 % 4.8 %
-------------------++------------------------------------------------------------------
|| 100.0 % 0 0 100.0 % 100.0 % 100.0 %
Hide all but the Total/Average columns:
$ hledger balance expenses -DTAS --summary-only
Balance changes in 2025-01-01..2025-01-04:
|| Total Average
===================++===================
expenses:rent || 800 USD 200 USD
expenses:food || 200 USD 50 USD
expenses:supplies || 50 USD 12 USD
-------------------++-------------------
|| 1050 USD 262 USD
Show the daily changes in asset and liability accounts:
$ hledger balance assets liabilities -D
Balance changes in 2025-01-01..2025-01-04:
|| 2025-01-01 2025-01-02 2025-01-03 2025-01-04
=========================++================================================
assets:bank:checking || 200 USD 1000 USD -200 USD -250 USD
assets:cash || 100 USD 0 0 0
liabilities:credit card || -400 USD 0 200 USD 0
-------------------------++------------------------------------------------
|| -100 USD 1000 USD 0 -250 USD
It’s important to note that despite its name, the “balance” command shows balance changes by default.
But it can also show the resulting balances, if you add the -H/--historical flag.
Notice the change of title:
$ hledger balance assets liabilities -DH
Ending balances (historical) in 2025-01-01..2025-01-04:
|| 2025-01-01 2025-01-02 2025-01-03 2025-01-04
=========================++================================================
assets:bank:checking || 200 USD 1200 USD 1000 USD 750 USD
assets:cash || 100 USD 100 USD 100 USD 100 USD
liabilities:credit card || -400 USD -400 USD -200 USD -200 USD
-------------------------++------------------------------------------------
|| -100 USD 900 USD 900 USD 650 USD
This is especially important if you restrict the report by date; it ensures that balances from earlier postings are included, to show correct historical balances (as you would see in bank statements):
$ hledger balance assets liabilities -DH date:2025-01-03..
Ending balances (historical) in 2025-01-03..2025-01-04:
|| 2025-01-03 2025-01-04
=========================++========================
assets:bank:checking || 1000 USD 750 USD
assets:cash || 100 USD 100 USD
liabilities:credit card || -200 USD -200 USD
-------------------------++------------------------
|| 900 USD 650 USD
Colons in account names indicate subaccounts. So we can show balances reports in tree mode, showing the account hierarchy. In tree mode, the changes or balances of parent accounts include the changes or balances of subaccounts:
$ hledger balance assets liabilities -DHt --no-elide
Ending balances (historical) in 2025-01-01..2025-01-04:
|| 2025-01-01 2025-01-02 2025-01-03 2025-01-04
===============++================================================
assets || 300 USD 1300 USD 1100 USD 850 USD
bank || 200 USD 1200 USD 1000 USD 750 USD
checking || 200 USD 1200 USD 1000 USD 750 USD
cash || 100 USD 100 USD 100 USD 100 USD
liabilities || -400 USD -400 USD -200 USD -200 USD
credit card || -400 USD -400 USD -200 USD -200 USD
---------------++------------------------------------------------
|| -100 USD 900 USD 900 USD 650 USD
Usually we don’t bother with --no-elide; then hledger will collapse
some identical lines to save space:
$ hledger balance assets liabilities -DHt
Ending balances (historical) in 2025-01-01..2025-01-04:
|| 2025-01-01 2025-01-02 2025-01-03 2025-01-04
=========================++================================================
assets || 300 USD 1300 USD 1100 USD 850 USD
bank:checking || 200 USD 1200 USD 1000 USD 750 USD
cash || 100 USD 100 USD 100 USD 100 USD
liabilities:credit card || -400 USD -400 USD -200 USD -200 USD
-------------------------++------------------------------------------------
|| -100 USD 900 USD 900 USD 650 USD
We can use --depth NUM or just -NUM to summarise the report down to a certain account depth.
Depth 1 means just the top level accounts:
$ hledger balance assets liabilities -DHt -1
Ending balances (historical) in 2025-01-01..2025-01-04:
|| 2025-01-01 2025-01-02 2025-01-03 2025-01-04
=============++================================================
assets || 300 USD 1300 USD 1100 USD 850 USD
liabilities || -400 USD -400 USD -200 USD -200 USD
-------------++------------------------------------------------
|| -100 USD 900 USD 900 USD 650 USD
--transpose exchanges the rows and columns, useful eg when reports are very wide.
And -N hides the usual totals row:
$ hledger balance assets liabilities -DHN -1 --transpose
Ending balances (historical) in 2025-01-01..2025-01-04:
|| assets liabilities
============++=======================
2025-01-01 || 300 USD -400 USD
2025-01-02 || 1300 USD -400 USD
2025-01-03 || 1100 USD -200 USD
2025-01-04 || 850 USD -200 USD
balance has a lot of options.
Next, we’ll see two specialised balance-like reports that are easier to use.
(Part of hledger by example.)
balancesheet: assets and liabilities
balancesheet, also known as bs, shares many of the features of the balance command, but it is simpler:
it shows historical balances of asset and liability accounts, each in their own section.
This and the next command we’ll show are the two most important accounting reports.
$ hledger bs
Balance Sheet 2025-01-04
|| 2025-01-04
=========================++============
Assets ||
-------------------------++------------
assets:bank:checking || 750 USD
assets:cash || 100 USD
-------------------------++------------
|| 850 USD
=========================++============
Liabilities ||
-------------------------++------------
liabilities:credit card || 200 USD
-------------------------++------------
|| 200 USD
=========================++============
Net: || 650 USD
Balances at end of each day, in tree mode, down to account depth 2:
$ hledger bs -Dt -2
Daily Balance Sheet 2025-01-01..2025-01-04
|| 2025-01-01 2025-01-02 2025-01-03 2025-01-04
=========================++================================================
Assets ||
-------------------------++------------------------------------------------
assets || 300 USD 1300 USD 1100 USD 850 USD
bank || 200 USD 1200 USD 1000 USD 750 USD
cash || 100 USD 100 USD 100 USD 100 USD
-------------------------++------------------------------------------------
|| 300 USD 1300 USD 1100 USD 850 USD
=========================++================================================
Liabilities ||
-------------------------++------------------------------------------------
liabilities:credit card || 400 USD 400 USD 200 USD 200 USD
-------------------------++------------------------------------------------
|| 400 USD 400 USD 200 USD 200 USD
=========================++================================================
Net: || -100 USD 900 USD 900 USD 650 USD
There’s a variant of this report which also shows equity balances: balancesheetequity / bse.
(Part of hledger by example.)
incomestatement: revenues and expenses
incomestatement, also known as is, shares many of the features of the balance command, but it is simpler:
it shows changes in revenue (income) and expense accounts, each in their own section.
This and balancesheet are the two most important financial reports.
$ hledger is
Income Statement 2025-01-01..2025-01-04
|| 2025-01-01..2025-01-04
===================++========================
Revenues ||
-------------------++------------------------
revenues:salary || 1000 USD
-------------------++------------------------
|| 1000 USD
===================++========================
Expenses ||
-------------------++------------------------
expenses:food || 200 USD
expenses:rent || 800 USD
expenses:supplies || 50 USD
-------------------++------------------------
|| 1050 USD
===================++========================
Net: || -50 USD
Daily revenues and expenses, largest first, and their totals and daily averages:
$ hledger is -DSTA
Daily Income Statement 2025-01-01..2025-01-04
|| 2025-01-01 2025-01-02 2025-01-03 2025-01-04 Total Average
===================++===================================================================
Revenues ||
-------------------++-------------------------------------------------------------------
revenues:salary || 0 1000 USD 0 0 1000 USD 250 USD
-------------------++-------------------------------------------------------------------
|| 0 1000 USD 0 0 1000 USD 250 USD
===================++===================================================================
Expenses ||
-------------------++-------------------------------------------------------------------
expenses:rent || 800 USD 0 0 0 800 USD 200 USD
expenses:food || 0 0 0 200 USD 200 USD 50 USD
expenses:supplies || 0 0 0 50 USD 50 USD 12 USD
-------------------++-------------------------------------------------------------------
|| 800 USD 0 0 250 USD 1050 USD 262 USD
===================++===================================================================
Net: || -800 USD 1000 USD 0 -250 USD -50 USD -12 USD
(Part of hledger by example.)
Reading CSV files
Most banks and financial institutions provide data in CSV (comma-separated values, or character-separated values) format. hledger can read from any CSV (or SSV, TSV, or other character-separated) file representing transactions. Eg:
"Date","Notes","Amount"
"2023/2/22","DEPOSIT","50.00"
"2023/2/23","TRANSFER TO SAVINGS","-10.00"
# bank.csv.rules # this rules file tells hledger how to read bank.csv
skip 1
fields date, description, amount
currency $
account1 assets:bank:checking
if WHOLE FOODS
account2 expenses:food
if (TO|FROM) SAVINGS
account2 assets:bank:savings
$ hledger -f bank.csv print
2023-02-22 DEPOSIT
assets:bank:checking $50.00
income:unknown $-50.00
2023-02-23 TRANSFER TO SAVINGS
assets:bank:checking $-10.00
assets:bank:savings $10.00
See also: Tutorial: Import CSV data
(Part of hledger by example.)
Importing new transactions
The import command detects just the new transactions in some input files, and adds them to the main journal.
This works with any of hledger’s input formats. But it’s especially useful with downloaded bank CSV files, which may contain records already seen in previous downloads. It means you don’t have to be careful about downloading exact date ranges.
Using bank.csv from the previous step: the first time we run import, it detects the two transactions:
$ hledger import bank.csv
imported 2 new transactions from bank.csv
And if we run it again on the same file, those transactions are not re-imported.
$ hledger import bank.csv
no new transactions found in bank.csv
$ hledger aregister checking
2023-01-01 opening balances as:ba:savings, as:.. $1000.00 $1000.00
2023-02-01 GOODWORKS CORP in:salary $1000.00 $2000.00
2023-02-22 DEPOSIT in:unknown $50.00 $2050.00
2023-02-23 TRANSFER TO SAVINGS as:ba:savings $-10.00 $2040.00
See also: Tutorial: Import CSV data
(Part of hledger by example.)
Reading timeclock files
hledger can also read time logs, in timeclock format:
i 2023/03/27 09:00:00 projects:a
o 2023/03/27 17:00:34
i 2023/03/31 22:21:45 personal:reading:online
o 2023/04/01 02:00:34
$ hledger -f 2023.timeclock register -D
2023-03-27 projects:a 8.01h 8.01h
2023-03-31 personal:reading:online 1.64h 9.65h
2023-04-01 personal:reading:online 2.01h 11.66h
(Part of hledger by example.)
Reading timedot files
Or in timedot format:
2023/2/1
biz:research .... ..
fos:hledger .... .... ....
2023/2/2
fos:ledger .
fos:haskell ..
biz:client1 .... ....
$ hledger -f 2023.timedot balance -tDTA # tree, Daily, Total, Average
Balance changes in 2023-02-01..2023-02-02:
|| 2023-02-01 2023-02-02 Total Average
============++==========================================
biz || 1.50 2.00 3.50 1.75
client1 || 0 2.00 2.00 1.00
research || 1.50 0 1.50 0.75
fos || 3.00 0.75 3.75 1.88
haskell || 0 0.50 0.50 0.25
hledger || 3.00 0 3.00 1.50
ledger || 0 0.25 0.25 0.12
------------++------------------------------------------
|| 4.50 2.75 7.25 3.62
(Part of hledger by example)
hledger
NAME
hledger - a robust, friendly plain text accounting app (command line version).
SYNOPSIS
hledger
orhledger COMMAND [OPTS] [ARGS]\
DESCRIPTION
hledger is a robust, user-friendly, cross-platform set of programs for tracking money, time, or any other commodity, using double-entry accounting and a simple, editable file format. hledger is inspired by and largely compatible with ledger(1), and largely interconvertible with beancount(1).
This manual is for hledger's command line interface, version 1.52. It
also describes the common options, file formats and concepts used by all
hledger programs. It might accidentally teach you some
bookkeeping/accounting as well! You don't need to know everything in
here to use hledger productively, but when you have a question about
functionality, this doc should answer it. It is detailed, so do skip
ahead or skim when needed. You can read it on hledger.org, or as an info
manual or man page on your system. You can also open a built-in copy, at
a point of interest, by runninghledger --man [CMD], hledger --info [CMD] or hledger help [TOPIC].
(And for shorter help, try hledger --tldr [CMD].)
The main function of the hledger CLI is to read plain text files
describing financial transactions, crunch the numbers, and print a
useful report on the terminal (or save it as HTML, CSV, JSON or SQL).
Many reports are available, as subcommands. hledger will also detect
other hledger-* executables as extra subcommands.
hledger usually reads from (and appends to) a journal file specified by
the LEDGER_FILE environment variable (defaulting to
$HOME/.hledger.journal); or you can specify files with -f options.
It can also read timeclock files, timedot files, or any CSV/SSV/TSV file
with a date field.
Here is a small journal file describing one transaction:
2015-10-16 bought food
expenses:food $10
assets:cash
Transactions are dated movements of money (etc.) between two or more
accounts: bank accounts, your wallet, revenue/expense categories,
people, etc. You can choose any account names you wish, using : to
indicate subaccounts. There must be at least two spaces between account
name and amount. Positive amounts are inflow to that account (debit),
negatives are outflow from it (credit). (Some reports show revenue,
liability and equity account balances as negative numbers as a result;
this is normal.)
hledger’s add command can help you add transactions, or you can install other data entry UIs like hledger-web or hledger-iadd. For more extensive/efficient changes, use a text editor: Emacs + ledger-mode, VIM + vim-ledger, or VS Code + hledger-vscode are some good choices (see https://hledger.org/editors.html).
To get started, run hledger add and follow the prompts, or save some
entries like the above in $HOME/.hledger.journal, then try commands
like:
$ hledger print -x
$ hledger aregister assets
$ hledger balance
$ hledger balancesheet
$ hledger incomestatement
Run hledger to list the commands. See also the "Starting a journal
file" and "Setting opening balances" sections in PART 5: COMMON
TASKS.
PART 1: USER INTERFACE
Input
hledger reads one or more data files, each time you run it. You can
specify a file with -f, like so
$ hledger -f FILE [-f FILE2 ...] print
Files are most often in hledger's journal format, with the
.journal file extension (.hledger or .j also work); these files
describe transactions, like an accounting general journal.
When no file is specified, hledger looks for .hledger.journal in your
home directory.
But most people prefer to keep financial files in a dedicated folder,
perhaps with version control. Also, starting a new journal file each
year is common (it's not required, but helps keep things fast and
organised). So we usually configure a different journal file, by setting
the LEDGER_FILE environment variable, to something like
~/finance/2023.journal. For more about how to do that on your system,
see Common tasks > Setting LEDGER_FILE.
Text encoding
hledger expects non-ascii input to be decodable with the system
locale's text encoding. (For CSV/SSV/TSV files, this can be overridden
by the encoding CSV rule.)
So, trying to read non-ascii files which have the wrong text encoding,
or when no system locale is configured, will fail. To fix this,
configure your system locale appropriately, and/or convert the files to
your system's text encoding (using iconv on unix, or powershell or
notepad on Windows). See Install: Text
encoding for more tips.
hledger's output will use the system locale's encoding.
hledger's docs and example files mostly use UTF-8 encoding.
Data formats
Usually the data file is in hledger's journal format, but it can be in any of the supported file formats, which currently are:
| Reader: | Reads: | Automatically used for files with extensions: |
|---|---|---|
journal | hledger journal files and some Ledger journals, for transactions | .journal .j .hledger .ledger |
timeclock | timeclock files, for precise time logging | .timeclock |
timedot | timedot files, for approximate time logging | .timedot |
csv | Comma- or other delimiter-separated values, for data import | .csv |
ssv | Semicolon separated values | .ssv |
tsv | Tab separated values | .tsv |
rules | CSV/SSV/TSV/other separated values, alternate way | .rules |
These formats are described in more detail below.
hledger detects the format automatically based on the file extensions
shown above. If it can't recognise the file extension, it assumes
journal format. So for non-journal files, it's important to use a
recognised file extension, so as to either read successfully or to show
relevant error messages.
You can also force a specific reader/format by prefixing the file path with the format and a colon. Eg, to read a .dat file containing tab separated values:
$ hledger -f tsv:/some/file.dat stats
Standard input
The file name - means standard input:
$ cat FILE | hledger -f- print
If reading non-journal data in this way, you'll need to write the
format as a prefix, like timeclock: here:
$ echo 'i 2009/13/1 08:00:00' | hledger print -f timeclock:-
Multiple files
You can specify multiple -f options, to read multiple files as one big
journal. When doing this, note that certain features (described below)
will be affected:
- Balance assertions will not see the effect of transactions in previous files. (Usually this doesn't matter as each file will set the corresponding opening balances.)
- Some directives will not affect previous or subsequent files.
If needed, you can work around these by using a single parent file which
includes the others, or concatenating the files
into one, eg: cat a.journal b.journal | hledger -f- CMD.
Strict mode
hledger checks input files for valid data. By default, the most important errors are detected, while still accepting easy journal files without a lot of declarations:
- Are the input files parseable, with valid syntax ?
- Are all transactions balanced ?
- Do all balance assertions pass ?
With the -s/--strict flag, additional checks are performed:
- Are all accounts posted to, declared with an
accountdirective ? (Account error checking) - Are all commodities declared with a
commoditydirective ? (Commodity error checking) - Are all commodity conversions declared explicitly ?
You can use the check command to run individual checks - the ones listed above and some more.
Commands
hledger provides various subcommands for getting things done. Most of
these commands do not change the journal file; they just read it and
output a report. A few commands assist with adding data and file
management. Some often-used commands are add, print, register,
balancesheet and incomestatement.
To show a summary of commands, run hledger with no arguments. You can
see the same commands summary at the start of PART 4:
COMMANDS below.
To use a particular command, run hledger CMD [CMDOPTS] [CMDARGS],
-
CMD is the full command name, or its standard abbreviation shown in the commands list, or any unambiguous prefix of the name.
-
CMDOPTS are command-specific options, if any. Command-specific options must be written after the command name. Eg:
hledger print -x. -
CMDARGS are additional arguments to the command, if any. Most hledger commands accept arguments representing a query, to limit the data in some way. Eg:
hledger reg assets:checking.
To list a command's options, arguments, and documentation in the
terminal, run hledger CMD -h. Eg: hledger bal -h.
Add-on commands
In addition to the built-in commands, you can install add-on commands, which will also appear in hledger's commands list. Some of these can be installed as separate packages; others can be found in hledger's bin/ directory, documented at https://hledger.org/scripts.html.
Add-on commands are programs or scripts in your shell's PATH, whose name starts with "hledger-" and ends with no extension or a recognised extension (".bat", ".com", ".exe", ".hs", ".js", ".lhs", ".lua", ".php", ".pl", ".py", ".rb", ".rkt", or ".sh"), and (on unix and mac) which has executable permission for the current user.
You can run add-on commands directly: hledger-ui --watch.
Or you can run them with hledger, like built-in commands:
hledger ui --watch. In this case hledger's config file will be used,
so you can set custom options for the addon there. (Before hledger 1.50,
an -- argument was needed before addon options, but not any more.)
Options
Run hledger -h to see general command line help. Options can be
written either before or after the command name. These options are
specific to the hledger CLI:
Flags:
--conf=CONFFILE Use extra options defined in this config file. If
not specified, searches upward and in XDG config
dir for hledger.conf (or .hledger.conf in $HOME).
-n --no-conf ignore any config file
And the following general options are common to most hledger commands:
General input/data transformation flags:
-f --file=[FMT:]FILE Read data from FILE, or from stdin if FILE is -,
inferring format from extension or a FMT: prefix.
Can be specified more than once. If not specified,
reads from $LEDGER_FILE or $HOME/.hledger.journal.
--rules=RULESFILE Use rules defined in this rules file for
converting subsequent CSV/SSV/TSV files. If not
specified, uses FILE.csv.rules for each FILE.csv.
--alias=A=B|/RGX/=RPL transform account names from A to B, or by
replacing regular expression matches
--auto generate extra postings by applying auto posting
rules ("=") to all transactions
--forecast[=PERIOD] Generate extra transactions from periodic rules
("~"), from after the latest ordinary transaction
until 6 months from now. Or, during the specified
PERIOD (the equals is required). Auto posting rules
will also be applied to these transactions. In
hledger-ui, also make future-dated transactions
visible at startup.
-I --ignore-assertions don't check balance assertions by default
--txn-balancing=... how to check that transactions are balanced:
'old': use global display precision
'exact': use transaction precision (default)
--infer-costs infer conversion equity postings from costs
--infer-equity infer costs from conversion equity postings
--infer-market-prices infer market prices from costs
--pivot=TAGNAME use a different field or tag as account names
-s --strict do extra error checks (and override -I)
--verbose-tags add tags indicating generated/modified data
General output/reporting flags (supported by some commands):
-b --begin=DATE include postings/transactions on/after this date
-e --end=DATE include postings/transactions before this date
(with a report interval, will be adjusted to
following subperiod end)
-D --daily multiperiod report with 1 day interval
-W --weekly multiperiod report with 1 week interval
-M --monthly multiperiod report with 1 month interval
-Q --quarterly multiperiod report with 1 quarter interval
-Y --yearly multiperiod report with 1 year interval
-p --period=PERIODEXP set begin date, end date, and/or report interval,
with more flexibility
--today=DATE override today's date (affects relative dates)
--date2 match/use secondary dates instead (deprecated)
-U --unmarked include only unmarked postings/transactions
-P --pending include only pending postings/transactions
-C --cleared include only cleared postings/transactions
(-U/-P/-C can be combined)
-R --real include only non-virtual postings
-E --empty Show zero items, which are normally hidden.
In hledger-ui & hledger-web, do the opposite.
--depth=DEPTHEXP if a number (or -NUM): show only top NUM levels
of accounts. If REGEXP=NUM, only apply limiting to
accounts matching the regular expression.
-B --cost show amounts converted to their cost/sale amount
-V --market Show amounts converted to their value at period
end(s) in their default valuation commodity.
Equivalent to --value=end.
-X --exchange=COMM Show amounts converted to their value at period
end(s) in the specified commodity.
Equivalent to --value=end,COMM.
--value=WHEN[,COMM] show amounts converted to their value on the
specified date(s) in their default valuation
commodity or a specified commodity. WHEN can be:
'then': value on transaction dates
'end': value at period end(s)
'now': value today
YYYY-MM-DD: value on given date
-c --commodity-style=S Override a commodity's display style.
Eg: -c '.' or -c '1.000,00 EUR'
--pretty[=YN] Use box-drawing characters in text output? Can be
'y'/'yes' or 'n'/'no'.
If YN is specified, the equals is required.
General help flags:
-h --help show command line help
--tldr show command examples with tldr
--info show the manual with info
--man show the manual with man
--version show version information
--debug=[1-9] show this much debug output (default: 1)
--pager=YN use a pager when needed ? y/yes (default) or n/no
--color=YNA --colour use ANSI color ? y/yes, n/no, or auto (default)
Usually hledger accepts any unambiguous flag prefix, eg you can write
--tl instead of --tldr or --dry instead of --dry-run.
You can combine short flags which don't take arguments, eg you can
write -MAST instead of -M -A -S -T. Flags requiring an argument
can't be combined in this way (-If FILE won't work).
If the same option appears more than once in a command line, usually the last (right-most) wins. Similarly, if mutually exclusive flags are used together, the right-most wins. (When flags are mutually exclusive, they'll usually have a group prefix in --help.)
With most commands, arguments are interpreted as a hledger query which filter the data. Some queries can be expressed either with options or with arguments.
Below are more tips for using the command line interface - feel free to skip these until you need them.
Special characters
In commands you type at the command line, certain characters have special meaning and sometimes need to be "escaped" or "quoted", by prefixing backslashes or enclosing in quotes.
If you are able to minimise the use of special characters in your data,
you won't have to deal with this as much. For example, you could use
hyphen - or underscore _ instead of spaces in account names, and you
could use the USD currency code instead of the $ currency symbol in
amounts.
But if you prefer to use spaced account names and $, it's fine. Just
be aware of this topic so you can check this doc when needed. (These
examples are mostly tested on unix; some details might need to be
adapted if you're on Windows.)
Escaping shell special characters
These are some characters which may have special meaning to your shell (the program which interprets command lines):
- SPACE,
<,>,(,),|,\,% $if followed by a word character
So for example, to match an account name containing spaces, like "credit card", don't write:
$ hledger register credit card
Instead, enclose the name in single quotes:
$ hledger register 'credit card'
On unix or in Windows powershell, if you use double quotes your shell
will silently treat $ as variable interpolation. So you should
probably avoid double quotes, unless you want that behaviour, eg in a
script:
$ hledger register "assets:$SOMEACCT"
But in an older Windows CMD.EXE window, you must use double quotes:
C:\Users\Me> hledger register "credit card"
On unix or in Windows powershell, as an alternative to quotes you can write a backslash before each special character:
$ hledger register credit\ card
Finally, since hledger's query arguments are regular
expressions (described below), you could also
fill that gap with . which matches any character:
$ hledger register credit.card
Escaping regular expression special characters
Some characters also have special meaning in regular expressions, which hledger's arguments often are. Those include:
.,^,$,[,],(,),|,\
To escape one of these, write \ before it. But note this is in
addition to the shell escaping above. So for characters which are
special to both shell and regular expressions, like \ and $, you
will sometimes need two levels of escaping.
For example, a balance report that uses a cur: query restricting it to
just the $ currency, should be written like this:
$ hledger balance cur:\\$
Explanation:
- Add a backslash
\before the dollar sign$to protect it from regular expressions (so it will be matched literally with no special meaning). - Add another backslash before that backslash, to protect it from the shell (so the shell won't consume it).
$doesn't need to be protected from the shell in this case, because it's not followed by a word character; but it would be harmless to do so.
But here's another way to write that, which tends to be easier: add backslashes to escape from regular expressions, then enclose with quotes to escape from the shell:
$ hledger balance cur:'\$'
Escaping in other situations
hledger options and arguments are sometimes used in places other than the command line, where the escaping/quoting rules are different. For example, backslash-quoting may not be available. Here's a quick reference:
| In unix shell | Use single quotes and/or backslash (or double quotes for variable interpolation) |
In Windows powershell | Use single quotes (or double quotes for variable interpolation) |
In Windows cmd | Use double quotes |
| In hledger-ui's filter prompt | Use single or double quotes |
| In hledger-web's search form | Use single or double quotes |
| In an argument file | Don't use spaces, don't shell-escape, do regex-escape, write one argument/option per line |
| In a config file | Use single or double quotes, and enclose the whole argument ( 'desc:a b' not desc:'a b') |
In ghci (the Haskell REPL) | Use double quotes, and enclose the whole argument |
Unicode characters
hledger is expected to handle non-ascii characters correctly:
-
they should be parsed correctly in input files and on the command line, by all hledger tools (add, iadd, hledger-web's search/add/edit forms, etc.)
-
they should be displayed correctly by all hledger tools, and on-screen alignment should be preserved.
This requires a well-configured environment. Here are some tips:
-
A system locale must be configured, which can decode the characters being used. This is essential - see Text encoding and Install: Text encoding.
-
Your terminal software (eg Terminal.app, iTerm, CMD.exe, xterm..) must support unicode. On Windows, you may need to use Windows Terminal.
-
The terminal must be using a font which includes the required unicode glyphs.
-
The terminal should be configured to display wide characters as double width (for report alignment).
-
On Windows, for best results you should run hledger in the same kind of environment in which it was built. Eg hledger built in the standard CMD.EXE environment (like the binaries on our download page) might show display problems when run in a cygwin or msys terminal, and vice versa. (See eg #961).
Regular expressions
A regular expression
(regexp) is a small piece of text where certain characters (like .,
^, $, +, *, (), |, [], \) have special meanings, forming
a tiny language for matching text precisely - very useful in hledger and
elsewhere. To learn all about them, visit
regular-expressions.info.
hledger supports regexps whenever you are entering a pattern to match
something, eg in query arguments, account
aliases, CSV if rules, hledger-web's
search form, hledger-ui's / search, etc. You may need to wrap them in
quotes, especially at the command line (see Special
characters above). Here are some examples:
Account name queries (quoted for command line use):
Regular expression: Matches:
------------------- ------------------------------------------------------------
bank assets:bank, assets:bank:savings, expenses:art:banksy, ...
:bank assets:bank:savings, expenses:art:banksy
:bank: assets:bank:savings
'^bank' none of those ( ^ matches beginning of text )
'bank$' assets:bank ( $ matches end of text )
'big \$ bank' big $ bank ( \ disables following character's special meaning )
'\bbank\b' assets:bank, assets:bank:savings ( \b matches word boundaries )
'(sav|check)ing' saving or checking ( (|) matches either alternative )
'saving|checking' saving or checking ( outer parentheses are not needed )
'savings?' saving or savings ( ? matches 0 or 1 of the preceding thing )
'my +bank' my bank, my bank, ... ( + matches 1 or more of the preceding thing )
'my *bank' mybank, my bank, my bank, ... ( * matches 0 or more of the preceding thing )
'b.nk' bank, bonk, b nk, ... ( . matches any character )
Some other queries:
desc:'amazon|amzn|audible' Amazon transactions
cur:EUR amounts with commodity symbol containing EUR
cur:'\$' amounts with commodity symbol containing $
cur:'^\$$' only $ amounts, not eg AU$ or CA$
cur:....? amounts with 4-or-more-character symbols
tag:.=202[1-3] things with any tag whose value contains 2021, 2022 or 2023
Account name aliases: accept . instead of : as account separator:
alias /\./=: replaces all periods in account names with colons
Show multiple top-level accounts combined as one:
--alias='/^[^:]+/=combined' ( [^:] matches any character other than : )
Show accounts with the second-level part removed:
--alias '/^([^:]+):[^:]+/ = \1'
match a top-level account and a second-level account
and replace those with just the top-level account
( \1 in the replacement text means "whatever was matched
by the first parenthesised part of the regexp"
CSV rules: match CSV records containing dining-related MCC codes:
if \?MCC581[124]
Match CSV records with a specific amount around the end/start of month:
if %amount \b3\.99
& %date (29|30|31|01|02|03)$
hledger's regular expressions
hledger's regular expressions come from the regex-tdfa library. If they're not doing what you expect, it's important to know exactly what they support:
- they are case insensitive
- they are infix matching (they do not need to match the entire thing being matched)
- they are POSIX ERE (extended regular expressions)
- they also support GNU word
boundaries
(
\b,\B,\<,\>) - backreferences
are supported when doing text replacement in account
aliases or CSV rules, where
backreferences
can be used in the replacement string to reference capturing
groups in the
search regexp. Otherwise, if you write
\1, it will match the digit1. - they do not support lazy
quantifiers
(
*?), mode modifiers ((?s)), character classes (\w,\d), or anything else not mentioned above. - they may not (I'm guessing not) properly support right-to-left or bidirectional text.
Some things to note:
-
In the
aliasdirective and--aliasoption, regular expressions must be enclosed in forward slashes (/REGEX/). Elsewhere in hledger, these are not required. -
In queries, to match a regular expression metacharacter like
$as a literal character, prepend a backslash. Eg to search for amounts with the dollar sign in hledger-web, writecur:\$. -
On the command line, some metacharacters like
$have a special meaning to the shell and so must be escaped at least once more. See Special characters.
Argument files
You can save a set of command line options and arguments in a file, and
then use them by writing @FILE.args as a hledger command argument. The
.args file extension is conventional, but not required. In an argument
file,
- Each line can contain one argument, flag, or option.
- Blank lines or lines beginning with
#are ignored. - An option's flag and value should be joined by
=. - An option value or an argument may contain spaces. Don't use single or double quotes.
- And generally, use one less level of quoting/escaping than at the
command line. Eg
cur:\$, notcur:\\$as on the command line.
For example:
# cash.args
assets:cash
assets:charles schwab:sweep
cur:\$
-c=$1.
$ hledger bal @cash.args
Config files
With hledger 1.40+, you can save extra command line options and arguments in a more featureful hledger config file. Here's a small example:
# General options are listed first, and used with hledger commands that support them.
--pretty
# Options following a `[COMMAND]` heading are used with that hledger command only.
[print]
--explicit --infer-costs
To use a config file, specify it with the --conf option. Its options
will be inserted near the start of your command line, so you can
override them with command line options if needed.
Or, you can set up an automatic config file that is used whenever you
run hledger, by creating hledger.conf in the current directory or
above, or .hledger.conf in your home directory (~/.hledger.conf), or
hledger.conf in your XDG config directory
(~/.config/hledger/hledger.conf).
Here is another example config you could start with: https://github.com/simonmichael/hledger/blob/master/hledger.conf.sample
You can put not only options, but also arguments in a config file. If
the first word in a config file's top (general) section does not begin
with a dash (eg: print), it is treated as the command argument
(overriding any argument on the command line).
On unix machines, you can add a shebang line at the top of a config
file, set executable permission on the file, and use it like a script.
Eg (the -S is needed on some operating systems):
#!/usr/bin/env -S hledger --conf
You can ignore config files by adding the -n/--no-conf flag to the
command line. This is useful when using hledger in scripts, or when
troubleshooting. When both --conf and --no-conf options are used,
the right-most wins.
To inspect the processing of config files, use --debug or --debug=8.
Or, run the setup command, which will display any active config files.
(setup is not affected by config files itself, unlike other commands.)
Warning!
There aren't many hledger features that need a warning, but this is one!
Automatic config files, while convenient, also make hledger less predictable and dependable. It's easy to make a config file that changes a report's behaviour, or breaks your hledger-using scripts/applications, in ways that will surprise you later.
If you don't want this,
- Just don't create a hledger.conf file on your machine.
- Also be alert to downloaded directories which may contain a hledger.conf file.
- Also if you are sharing scripts or examples or support, consider that others may have a hledger.conf file.
Conversely, once you decide to use this feature, try to remember:
- Whenever a hledger command does not work as expected, try it again
with
-n(--no-conf) to see if a config file was to blame. - Whenever you call hledger from a script, consider whether that call
should use
-nor not. - Be conservative about what you put in your config file; try to consider the effect on all your reports.
- To troubleshoot the effect of config files, run with
--debugor--debug 8.
The config file feature was added in hledger 1.40.
Shell completions
If you use the bash or zsh shells, you can optionally set up
context-sensitive autocompletion for hledger command lines. Try pressing
hledger<SPACE><TAB><TAB> (should list all hledger commands) or
hledger reg acct:<TAB><TAB> (should list your top-level account
names). If completions aren't working, or for more details, see
Install > Shell completions.
Output
Output destination
hledger commands send their output to the terminal by default. You can of course redirect this, eg into a file, using standard shell syntax:
$ hledger print > foo.txt
Some commands (print, register, stats, the balance commands) also
provide the -o/--output-file option, which does the same thing
without needing the shell. Eg:
$ hledger print -o foo.txt
$ hledger print -o - # write to stdout (the default)
Output format
Some commands offer other kinds of output, not just text on the terminal. Here are those commands and the formats currently supported:
| command | txt | html | csv/tsv | fods | beancount | sql | json |
|---|---|---|---|---|---|---|---|
| aregister | Y | Y | Y | Y | Y | ||
| balance | Y | Y | Y | Y | Y | ||
| balancesheet | Y | Y | Y | Y | Y | ||
| balancesheetequity | Y | Y | Y | Y | Y | ||
| cashflow | Y | Y | Y | Y | Y | ||
| incomestatement | Y | Y | Y | Y | Y | ||
| Y | Y | Y | Y | Y | Y | Y | |
| register | Y | Y | Y | Y | Y |
You can also see which output formats a command supports by running
hledger CMD -h and looking for the -O/--output-format=FMT option,
You can select the output format by using that option:
$ hledger print -O csv # print CSV to standard output
or by choosing a suitable filename extension with the
-o/--output-file=FILE.FMT option:
$ hledger balancesheet -o foo.csv # write CSV to foo.csv
The -O option can be combined with -o to override the file extension
if needed:
$ hledger balancesheet -o foo.txt -O csv # write CSV to foo.txt
Here are some notes about the various output formats.
Text output
This is the default: human readable, plain text report output, suitable for viewing with a monospace font in a terminal. If your data contains unicode or wide characters, you'll need a terminal and font that render those correctly. (This can be challenging on MS Windows.)
Some reports (register, aregister) will normally use the full window
width. If this isn't working or you want to override it, you can use
the -w/--width option.
Balance reports (balance, balancesheet, incomestatement...) use
whatever width they need. Multi-period multi-currency reports can often
be wider than the window. Besides using a pager, helpful techniques for
this situation include --layout=bare, -X COMM, cur:,
--transpose, --tree, --depth, --drop, switching to html output,
etc.
Box-drawing characters
hledger draws simple table borders by default, to minimise the risk of display problems caused by a terminal/font not supporting box-drawing characters.
But your terminal and font probably do support them, so we recommend
using the --pretty flag to show prettier tables in the terminal. This
is a good flag to add to your hledger config file.
Colour
hledger tries to automatically detect ANSI colour and text styling support and use it when appropriate. (Currently, it is used rather minimally: some reports show negative numbers in red, and help output uses bold text for emphasis.)
You can override this by setting the NO_COLOR environment variable to
disable it, or by using the --color/--colour option, perhaps in your
config file, with a y/yes or n/no value to force it on or off.
Paging
In unix-like environments, when displaying large output (in any output
format) in the terminal, hledger tries to use a pager when appropriate.
(You can disable this with the --pager=no option, perhaps in your
config file.)
The pager shows one page of text at a time, and lets you scroll around
to see more. While it is active, usually SPACE shows the next page,
h shows help, and q quits. The home/end/page up/page down/cursor
keys, and mouse scrolling, may also work.
hledger will use the pager specified by the PAGER environment
variable, otherwise less if available, otherwise more if available.
(With one exception: hledger help -p TOPIC will always use less, so
that it can scroll to the topic.)
The pager is expected to display hledger's ANSI colour and text styling. If you see junk characters, you might need to configure your pager to handle ANSI codes. Or you could disable colour as described above.
If you are using the less
pager, hledger tries
to provide a consistently pleasant experience by running it with some
extra options added to your LESS environment variable:
--chop-long-lines --hilite-unread --ignore-case --no-init --quit-if-one-screen --shift=8 --squeeze-blank-lines --use-backslash
and when colour output is enabled:
--RAW-CONTROL-CHARS
You can prevent this by setting your preferred options in the
HLEDGER_LESS variable, which will be used instead of LESS.
HTML output
HTML output can be styled by an optional hledger.css file in the same
directory.
HTML output will be a HTML fragment, not a complete HTML document. Like other hledger output, for non-ascii characters it will use the system locale's text encoding (see Text encoding).
CSV / TSV output
In CSV or TSV output, digit group marks (such as thousands separators) are disabled automatically.
FODS output
FODS is the OpenDocument
Spreadsheet format as plain XML, as accepted by LibreOffice and
OpenOffice. If you use their spreadsheet applications, this is better
than CSV because it works across locales (decimal point vs. decimal
comma, character encoding stored in XML header, thus no problems with
umlauts), it supports fixed header rows and columns, cell types (string
vs. number vs. date), separation of number and currency (currency is
displayed but the cell type is still a number accessible for
computation), styles (bold), borders. Btw. you can still extract CSV
from FODS/ODS using various utilities like libreoffice --headless or
ods2csv.
Beancount output
This is Beancount's journal format. You can use this to export your hledger data to Beancount, eg to use the Fava web app.
hledger will try to adjust your data to suit Beancount, automatically. Be cautious and check the conversion until you are confident it is good. If you plan to export to Beancount often, you may want to follow its conventions, for a cleaner conversion:
- use Beancount-friendly account names
- use currency codes instead of currency symbols
- use cost notation instead of equity conversion postings
- avoid virtual postings, balance assignments, and secondary dates.
There is one big adjustment you must handle yourself: for Beancount, the
top level account names must be Assets, Liabilities, Equity,
Income, and/or Expenses. You can use account
aliases to rewrite your account names temporarily, if
needed, as in this
hledger2beancount.conf
config file.
2024-12-20: Some more things not yet handled for you:
- P directives are not converted automatically - convert those yourself.
- Balance assignments are not converted (Beancount doesn't support them) - replace those with explicit amounts.
Beancount account names
Aside from the top-level names, hledger will adjust your account names
to make valid Beancount account
names,
by capitalising each part, replacing spaces with -, replacing other
unsupported characters with C<HEXBYTES>, prepending A to account
name parts which don't begin with a letter or digit, and appending :A
to account names which have only one part.
Beancount commodity names
hledger will adjust your commodity names to make valid Beancount
commodity/currency
names,
which must be 2-24 uppercase letters, digits, or ', ., _, -,
beginning with a letter and ending with a letter or digit. hledger will
convert known currency symbols to ISO 4217 currency
codes, capitalise
letters, replace spaces with -, replace other unsupported characters
with C<HEXBYTES>, and prepend or append C if needed.
Beancount virtual postings
Beancount doesn't allow virtual postings; if you have any, they will be omitted from beancount output.
Beancount metadata
hledger tags will be converted to Beancount
metadata
(except for tags whose name begins with _). Metadata names will be
adjusted to be Beancount-compatible: beginning with a lowercase letter,
at least two characters long, and with unsupported characters encoded.
Metadata values will use Beancount's string type.
In hledger, objects can have the same tag repeated with multiple values.
Eg an assets:cash account might have both type:Asset and type:Cash
tags. For Beancount these will be combined into one, with the values
combined, comma separated. Eg: type: "Asset, Cash".
Beancount costs
Beancount doesn't allow redundant costs and conversion postings as hledger does. If you have any of these, the conversion postings will be omitted. Currently we support at most one cost + conversion postings group per transaction.
Beancount operating currency
Declaring an operating currency (or several) improves Beancount and Fava reports. Currently hledger will declare each currency used in cost amounts as an operating currency. If needed, replace these with your own declaration, like
option "operating_currency" "USD"
SQL output
SQL output is expected to work at least with SQLite, MySQL and Postgres.
The SQL statements are expected to be executed in the empty database. If
you already have tables created via SQL output of hledger, you would
probably want to either clear data from these (via delete or
truncate SQL statements) or drop the tables completely before
import; otherwise your postings would be duplicated.
For SQLite, it is more useful if you modify the generated id field to
be a PRIMARY KEY. Eg:
$ hledger print -O sql | sed 's/id serial/id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL/g' | ...
This is not yet much used; feedback is welcome.
JSON output
Our JSON is rather large and verbose, since it is a faithful representation of hledger's internal data types. To understand its structure, read the Haskell type definitions, which are mostly in https://github.com/simonmichael/hledger/blob/master/hledger-lib/Hledger/Data/Types.hs. hledger-web's OpenAPI specification may also be relevant.
hledger stores numbers with sometimes up to 255 significant digits. This is too many digits for most JSON consumers, so in JSON output we round numbers to at most 10 decimal places. (We don't limit the number of integer digits.) If you find this causing problems, please let us know. Related: #1195
This is not yet much used; feedback is welcome.
Commodity styles
When displaying amounts, hledger infers a standard display style for each commodity/currency, as described below in Commodity display style.
If needed, this can be overridden by a -c/--commodity-style option
(except for cost amounts and amounts displayed by the
print command, which are always displayed with all decimal
digits). For example, the following will force dollar amounts to be
displayed as shown:
$ hledger print -c '$1.000,0'
This option can be repeated to set the display style for multiple commodities/currencies. Its argument is as described in the commodity directive. Note that omitting the commodity symbol will set the display style for just the no-symbol commodity, not all commodities.
In some cases hledger will adjust number formatting to improve their parseability (such as adding trailing decimal marks when needed).
Debug output
We intend hledger to be relatively easy to troubleshoot, introspect and
develop. You can add --debug[=N] to any hledger command line to see
additional debug output. N ranges from 1 (least output, the default) to
9 (maximum output). Typically you would start with 1 and increase until
you are seeing enough. Debug output goes to stderr, and is not affected
by -o/--output-file (unless you redirect stderr to stdout, eg:
2>&1). It will be interleaved with normal output, which can help
reveal when parts of the code are evaluated. To capture debug output in
a log file instead, you can usually redirect stderr, eg:
hledger bal --debug=3 2>hledger.log
(This option doesn't work in a config file yet.)
Environment
These environment variables affect hledger:
HLEDGER_LESS If less is your pager, this variable
specifies the less options hledger should use. (Otherwise, LESS +
custom options are used.)
LEDGER_FILE The default journal file, to be used when no -f/--file
option is provided. For example, it could be ~/finance/main.journal.
This can also be a glob pattern, eg ./2???.journal. (If the glob
matches multiple files, only the alphanumerically first one is used.) If
LEDGER_FILE points to a non-existent file, an error will be raised. If
the value is the empty string, it is ignored.
If LEDGER_FILE is not set and -f is not provided, the default journal
file is $HOME/.hledger.journal (or if a home directory can't be
detected, ./.hledger.journal).
See also Common tasks > Setting LEDGER_FILE.
NO_COLOR If this environment variable exists (with any value,
including empty), hledger will not use ANSI color codes in terminal
output, unless overridden by an explicit --color=y or --colour=y
option.
PART 2: DATA FORMATS
Journal
hledger's usual data source is a plain text file containing journal
entries in hledger journal format. If you're looking for a quick
reference, jump ahead to the journal cheatsheet
(or use the table of contents at https://hledger.org/hledger.html).
This file represents an accounting General
Journal. The .journal
file extension is most often used, though not strictly required. The
journal file contains a number of transaction entries, each describing a
transfer of money (or any commodity) between two or more named accounts,
in a simple format readable by both hledger and humans.
hledger's journal format is compatible with most of Ledger's journal format, but not all of it. The differences and interoperation tips are described at hledger and Ledger. With some care, and by avoiding incompatible features, you can keep your hledger journal readable by Ledger and vice versa. This can useful eg for comparing the behaviour of one app against the other.
You can use hledger without learning any more about this file; just use the add or web or import commands to create and update it.
Many users, though, edit the journal file with a text editor, and track changes with a version control system such as git. Editor add-ons such as ledger-mode or hledger-mode for Emacs, vim-ledger for Vim, and hledger-vscode for Visual Studio Code, make this easier, adding colour, formatting, tab completion, and useful commands. See Editors at hledger.org for the full list.
A hledger journal file can contain three kinds of thing: comment lines, transactions, and/or directives (including periodic transaction rules and auto posting rules). Understanding the journal file format will also give you a good understanding of hledger's data model. Here's a quick cheatsheet/overview, followed by detailed descriptions of each part.
Journal cheatsheet
# Here is the main syntax of hledger's journal format
# (omitting extra Ledger compatibility syntax).
###############################################################################
# 1. These are comment lines, for notes or temporarily disabling things.
; They begin with # or ;
comment
Or, lines can be enclosed within "comment" / "end comment".
This is a block of
commented lines.
end comment
# Some journal entries can have semicolon comments at end of line ; like this
# Some of them require 2 or more spaces before the semicolon.
###############################################################################
# 2. Directives customise processing or output in some way.
# You don't need any directives to get started.
# But they can add more error checking, or change how things are displayed.
# They begin with a word, letter, or symbol.
# They are most often placed at the top, before transactions.
account assets ; Declare valid account names and display order.
account assets:savings ; A subaccount. This one represents a bank account.
account assets:checking ; Another. Note, 2+ spaces after the account name.
account assets:receivable ; Accounting type is inferred from english names,
account passifs ; or declared with a "type" tag, type:L
account expenses ; type:X
; A follow-on comment line, indented.
account expenses:rent ; Expense and revenue categories are also accounts.
; Subaccounts inherit their parent's type.
commodity $0.00 ; Declare valid commodities and their display styles.
commodity 1.000,00 EUR
decimal-mark . ; The decimal mark used in this file (if ambiguous).
payee Whole Foods ; Declare a valid payee name.
tag trip ; Declare a valid tag name.
P 2024-03-01 AAPL $179 ; Declare a market price for AAPL in $ on this date.
include other.journal ; Include another journal file here.
# Declare a recurring "periodic transaction", for budget/forecast reports
~ monthly set budget goals ; <- Note, 2+ spaces before the description.
(expenses:rent) $1000
(expenses:food) $500
# Declare an auto posting rule, to modify existing transactions in reports
= revenues:consulting
liabilities:tax:2024:us *0.25 ; Add a tax liability & expense
expenses:tax:2024:us *-0.25 ; for 25% of the revenue.
###############################################################################
# 3. Transactions are what it's all about.
# They are dated events, usually movements of money between 2 or more accounts.
# They begin with a numeric date.
# Here is their basic shape:
#
# DATE DESCRIPTION ; The transaction's date and optional description.
# ACCOUNT1 AMOUNT ; A posting of an amount to/from this account, indented.
# ACCOUNT2 AMOUNT ; A second posting, balancing the first.
# ... ; More if needed. Amounts must sum to zero.
# ; Note, 2+ spaces between account names and amounts.
2024-01-01 opening balances ; At the start, declare pre-existing balances this way.
assets:savings $10000 ; Account names can be anything. lower case is easy to type.
assets:checking $1000 ; assets, liabilities, equity, revenues, expenses are common.
liabilities:credit card $-500 ; liabilities, equity, revenues balances are usually negative.
equity:start ; One amount can be left blank. $-10500 is inferred here.
; Some of these accounts we didn't declare above,
; so -s/--strict would complain.
2024-01-03 ! (12345) pay rent
; Additional transaction comment lines, indented.
; There can be a ! or * after the date meaning "pending" or "cleared".
; There can be a parenthesised (code) after the date/status.
; Amounts' sign shows direction of flow.
assets:checking $-500 ; Minus means removed from this account (credit).
expenses:rent $500 ; Plus means added to this account (debit).
; Keeping transactions in date order is optional (but helps error checking).
2024-01-02 Gringott's Bank | withdrawal ; Description can be PAYEE | NOTE
assets:bank:gold -10 gold
assets:pouch 10 gold
2024-01-02 shopping
expenses:clothing 1 gold
expenses:wands 5 gold
assets:pouch -6 gold
2024-01-02 receive gift
revenues:gifts -3 "Chocolate Frogs" ; Complex commodity symbols
assets:pouch 3 "Chocolate Frogs" ; must be in double quotes.
2024-01-15 buy some shares, in two lots ; Cost can be noted.
assets:investments:2024-01-15 2.0 AAAA @ $1.50 ; @ means per-unit cost
assets:investments:2024-01-15-02 3.0 AAAA @@ $4 ; @@ means total cost
; ^ Per-lot subaccounts are sometimes useful.
assets:checking $-7
2024-01-15 assert some account balances on this date
; Balances can be asserted in any transaction, with =, for extra error checking.
; Assertion txns like this one can be made with hledger close --assert --show-costs
;
assets:savings $0 = $10000
assets:checking $0 = $493
assets:bank:gold 0 gold = -10 gold
assets:pouch 0 gold = 4 gold
assets:pouch 0 "Chocolate Frogs" = 3 "Chocolate Frogs"
assets:investments:2024-01-15 0.0 AAAA = 2.0 AAAA @ $1.50
assets:investments:2024-01-15-02 0.0 AAAA = 3.0 AAAA @@ $4
liabilities:credit card $0 = $-500
2024-02-01 note some event, or a transaction not yet fully entered, on this date
; Postings are not required.
# Consistent YYYY-MM-DD date format is recommended,
# but you can use . or / and omit leading zeros if you prefer.
2024.01.01
2024/1/1
Comments
Lines in the journal will be ignored if they begin with a hash (#) or
a semicolon (;). (See also Other syntax.) hledger
will also ignore regions beginning with a comment line and ending with
an end comment line (or file end). Here's a suggestion for choosing
between them:
#for top-level notes;for commenting out things temporarilycommentfor quickly commenting large regions (remember it's there, or you might get confused)
Eg:
# a comment line
; another commentline
comment
A multi-line comment block,
continuing until "end comment" directive
or the end of the current file.
end comment
Some hledger entries can have same-line comments attached to them, from ; (semicolon) to end of line. See Transaction comments, Posting comments, and Account comments below.
Transactions
Transactions are the main unit of information in a journal file. They represent events, typically a movement of some quantity of commodities between two or more named accounts.
Each transaction is recorded as a journal entry, beginning with a simple date in column 0. This can be followed by any of the following optional fields, separated by spaces:
- a status character (empty,
!, or*) - a code (any short number or text, enclosed in parentheses)
- a description (any remaining text until end of line or a semicolon)
- a comment (any remaining text following a semicolon until end of line, and any following indented lines beginning with a semicolon)
- 0 or more indented posting lines, describing what was transferred and the accounts involved (indented comment lines are also allowed, but not blank lines or non-indented lines).
Here's a simple journal file containing one transaction:
2008/01/01 income
assets:bank:checking $1
income:salary $-1
Dates
Simple dates
Dates in the journal file use simple dates format: YYYY-MM-DD or
YYYY/MM/DD or YYYY.MM.DD, with leading zeros optional. The year may
be omitted, in which case it will be inferred from the context: the
current transaction, the default year set with a Y
directive, or the current date when the command is run.
Some examples: 2010-01-31, 2010/01/31, 2010.1.31, 1/31.
(The UI also accepts simple dates, as well as the more flexible smart dates documented in the hledger manual.)
Posting dates
You can give individual postings a different date from their parent
transaction, by adding a posting comment containing
a tag (see below) like ; date:DATE. (There's also a
Ledger-compatible syntax, ; [DATE], which
can be convenient.)
This is probably the best way to control posting dates precisely. Eg in this example the expense should appear in May reports, and the deduction from checking should be reported on 6/1 for easy bank reconciliation:
2015/5/30
expenses:food $10 ; food purchased on saturday 5/30
assets:checking ; bank cleared it on monday, date:6/1
$ hledger -f t.j register food
2015-05-30 expenses:food $10 $10
$ hledger -f t.j register checking
2015-06-01 assets:checking $-10 $-10
DATE should be a simple date; if the year is not
specified it will use the year of the transaction's date.
The date: tag must have a valid simple date value if it is present, eg
a date: tag with no value is not allowed.
Status
Transactions (or individual postings within a transaction) can have a status mark, which is a single character before the transaction description (or posting account name), separated from it by a space, indicating one of three statuses:
| mark | status |
|---|---|
| unmarked | |
! | pending |
* | cleared |
When reporting, you can filter by status with the -U/--unmarked,
-P/--pending, and -C/--cleared flags (and you can combine these, eg
-UP to match all except cleared things). Or you can use the status:,
status:!, and status:* queries, or the U, P, C keys in
hledger-ui.
(Note: in Ledger the "unmarked" state is called "uncleared"; in hledger we renamed it to "unmarked" for semantic clarity.)
Status marks are optional, but can be helpful eg for reconciling with real-world accounts. Some editor modes provide highlighting and shortcuts for working with status. Eg in Emacs ledger-mode, you can toggle transaction status with C-c C-e, or posting status with C-c C-c.
What "uncleared", "pending", and "cleared" actually mean is up to you. Here's one suggestion:
| status | meaning |
|---|---|
| uncleared | recorded but not yet reconciled; needs review |
| pending | tentatively reconciled (if needed, eg during a big reconciliation) |
| cleared | complete, reconciled as far as possible, and considered correct |
With this scheme, you would use -PC to see the current balance at your
bank, -U to see things which will probably hit your bank soon (like
uncashed checks), and no flags to see the most up-to-date state of your
finances.
Code
After the status mark, but before the description, you can optionally write a transaction "code", such as a check number or transaction id, enclosed in parentheses,
This has a few limitations: The code must not contain a closing parenthesis (or it will be truncated). Codes tend to disrupt alignment of the register report, making it harder to scan visually. And you can't store more than one value there per transaction. For these reasons you might want to avoid the code field and use tags(#tags] instead.
Description
After the date, status mark and/or code fields, the rest of the line (or
until a comment is begun with ;) is the transaction's description.
Here you can describe the transaction (called the "narration" in
traditional bookkeeping), or you can record a payee/payer name, or you
can leave it empty.
Transaction descriptions show up in print output and in register reports, and can be listed with the descriptions command.
You can query by description with desc:DESCREGEX, or
pivot on description with --pivot desc.
Payee and note
Sometimes people want a dedicated payee/payer field that can be queried
and checked more strictly. If you want that, you can write a | (pipe)
character in the description. This divides it into a "payee" field on
the left, and a "note" field on the right. (Either can be empty.)
You can query these with payee:PAYEEREGEX and note:NOTEREGEX, list
their values with the payees and notes commands, or
pivot on payee or note.
Note: in transactions with no | character, description, payee, and
note all have the same value. Once a | is added, they become distinct.
(If you'd like to change this behaviour, please propose it on the mail
list.)
If you want more strict error checking, you can declare the valid payee
names with payee directives, and then enforce these
with hledger check payees. (Note: because of the above, for
this you'll need to ensure every transaction description contains a |
and therefore a checkable payee name, even if it's empty.)
Transaction comments
Text following ;, after a transaction description, and/or on indented
lines immediately below it, form comments for that transaction. They are
reproduced by print but otherwise ignored, except they may contain
tags, which are not ignored.
2012-01-01 something ; a transaction comment
; a second line of transaction comment
expenses 1
assets
Postings
A posting is an addition of some amount to, or removal of some amount from, an account. Each posting line begins with at least one space or tab (2 or 4 spaces is common), followed by:
- (optional) a status character (empty,
!, or*), followed by a space - (required) an account name (any text, optionally including single spaces. If anything follows the account name on the same line, the account name must be ended by two or more spaces.)
- (optional) an amount
- (optional) a same-line posting comment, beginning
with a semicolon (
;).
If the amount is positive, it is being added to the account; if negative, it is being removed from the account.
The posting amounts in a transaction must sum up to zero, indicating that the inflows and outflows are equal. We call this a balanced transaction. (You can read more about the details of transaction balancing below.)
If no amount is written, it will be calculated automatically from the other postings in the transaction, so as to balance the transaction. In other words, in any transaction you can leave one posting amountless to save typing.
Debits and credits
The traditional accounting concepts of debit and credit of course exist in hledger, but we represent them with numeric sign. Positive and negative posting amounts represent debits and credits respectively.
You don't need to remember that, but if you would like to - eg for helping newcomers or for talking with your accountant - here's a handy mnemonic:
debit / plus / left / short wordscredit / minus / right / longer words
Account names
Accounts are the main way of categorising things in hledger. As in Double Entry Bookkeeping, they can represent real world accounts (such as a bank account), or more abstract categories such as "money spent on food" or "money borrowed from Frank".
Account names are flexible. They may be capitalised or not; they may contain letters, numbers, punctuation, symbols, or single spaces; they may be in any language.
Typically we use the five traditional accounting categories as the starting point for account names. In english they are:
assets, liabilities, equity, revenues, expenses
These will be discussed more in Account types below. In hledger docs you may see them referred to as A, L, E, R, X for short.
Two space delimiter
Note the two or more spaces delimiter that's sometimes required after account names. hledger's account names, inherited from Ledger, are very permissive; they may contain pretty much any kind of text, including single spaces and semicolons. Because of this, they must be terminated by two or more spaces if there is anything following them on the same line. For example, if an amount, balance assignment, or same-line comment follows an account name, they must be preceded by two or more spaces, else they would be considered part of the account name:
bad: assets:accounts receivable $10 ; <- too close!
good: assets:accounts receivable $10
bad: assets:accounts receivable =$1000 ; <- too close!
good: assets:accounts receivable =$1000
bad: assets:accounts receivable ; comment. <- too close!
good: assets:accounts receivable ; comment
This two-space delimiter appears in a few places in hledger, such as after account names in postings or account directives; also after the period expression in periodic transaction rules. When you are starting out, expect it to catch you out at least once. It's annoying sometimes, but it lets us use expressive account names while still keeping the syntax light.
Account hierarchy
For more precise reporting, we usually divide accounts into more
detailed subaccounts, subsubaccounts, and so on, by writing a full colon
between account name parts. For example, instead of writing assets and
expenses, we might write assets:bank:checking and expenses:food.
From these names hledger will infer this hierarchy of five accounts:
assets
assets:bank
assets:bank:checking
expenses
expenses:food
Or as an outline:
assets
bank
checking
expenses
food
hledger reports can summarise the account tree to any depth, so you can make your subcategories as detailed as you like. But don't go overboard, especially when getting started; simpler categories can be less work.
Other account name features
Enclosing the account name in parentheses or brackets, like
(expenses:food), enables a non-standard bookkeeping feature: virtual
postings.
Account names can be rewritten and restructured, temporarily or permanently, by account aliases.
Amounts
After the account name, there is usually an amount. (Remember: between account name and amount, there must be two or more spaces.)
hledger's amount format is flexible, supporting several international formats. Here are some examples. Amounts have a number (the "quantity"):
1
..and usually a currency symbol or commodity name (more on this below), to the left or right of the quantity, with or without a separating space:
$1
4000 AAPL
3 "green apples"
Amounts can be preceded by a minus sign (or a plus sign, though plus is the default), The sign can be written before or after a left-side commodity symbol:
-$1
$-1
One or more spaces between the sign and the number are acceptable when parsing (but they won't be displayed in output):
+ $1
$- 1
Scientific E notation is allowed:
1E-6
EUR 1E3
Decimal marks
A decimal mark can be written as a period or a comma:
1.23
1,23
Both of these are common in international number
formats,
so hledger is not biased towards one or the other. Because hledger also
supports digit group marks (eg thousands separators), this means that a
number like 1,000 or 1.000 containing just one period or comma is
ambiguous. In such cases, hledger by default assumes it is a decimal
mark, and will parse both of those as 1.
To help hledger parse such ambiguous numbers more accurately, if you use
digit group marks, we recommend declaring the decimal mark explicitly.
The best way is to add a decimal-mark
directive at the top of each data file, like this:
decimal-mark .
Or you can declare it per commodity with
commodity directives, described below.
hledger also accepts numbers like 10. with no digits after the decimal
mark (and will sometimes display numbers that way to disambiguate them -
see Trailing decimal marks).
Digit group marks
In the integer part of the amount quantity (left of the decimal mark), groups of digits can optionally be separated by a digit group mark - a comma or period (whichever is not used as decimal mark), or a space (several Unicode space variants, like no-break space, are also accepted). So these are all valid amounts in a journal file:
$1,000,000.00
EUR 2.000.000,00
INR 9,99,99,999.00
1 000 000.00 ; <- ordinary space
1 000 000.00 ; <- no-break space
Commodity
Amounts in hledger have both a "quantity", which is a signed decimal number, and a "commodity", which is a currency symbol, stock ticker, or any word or phrase describing something you are tracking.
If the commodity name contains non-letters (spaces, numbers, or
punctuation), you must always write it inside double quotes
("green apples", "ABC123").
If you write just a bare number, that too will have a commodity, with
name ""; we call that the "no-symbol commodity".
Actually, hledger combines these single-commodity amounts into more
powerful multi-commodity amounts, which are what it works with most of
the time. A multi-commodity amount could be, eg:
1 USD, 2 EUR, 3.456 TSLA. In practice, you will only see
multi-commodity amounts in hledger's output; you can't write them
directly in the journal file.
By default, the format of amounts in the journal influences how hledger displays them in output. This is explained in Commodity display style below.
Costs
In traditional double entry bookkeeping, to record a transaction where one commodity is exchanged for another, you add extra equity postings to balance the two commodities. Eg:
2026-01-01 buy euros
assets:dollars $-123
equity:conversion $123
equity:conversion €-100
assets:euros €100
hledger offers a more convenient @/@@ "cost notation" as an
alternative: instead of equity postings, you can write the "conversion
rate" or "transacted price" after a posting amount. hledger docs
generically call this "cost", whether buying or selling. It can be
written as either @ UNITPRICE or @@ TOTALPRICE. Eg you could write
the above as:
2026-01-01 buy euros
assets:dollars $-123
assets:euros €100 @ $1.23 ; unit cost (exchange rate)
or:
2026-01-01 buy euros
assets:dollars $-123
assets:euros €100 @@ $123 ; total cost
The cost should normally be a positive amount. Negative costs are supported, but can be confusing, as discussed at --infer-market-prices: market prices from transactions.
Costs participate in transaction balancing. Amounts are converted to their cost before checking if the transaction is balanced. You could also write the above less redundantly, like so:
2026-01-01 buy euros
assets:dollars ; $-123 is inferred
assets:euros €100 @ $1.23
or:
2026-01-01 buy euros
assets:dollars ; $-123 is inferred
assets:euros €100 @@ $123
or even:
2026-01-01 buy euros
assets:euros €100 ; @@ $123 is inferred
assets:dollars $-123
This last form works for transactions involving exactly two commodities,
with neither cost notation nor equity postings. Note, the order of
postings is significant: the cost will be attached to the first (top)
posting. So we had to switch the order of postings, to get the same
meaning as above. Also, this form is the easiest to make undetected
errors with; so it is rejected by hledger check balanced, and by
strict mode.
Advantages of cost notation:
- it's more compact and easier to read and write
- hledger reports can show such amounts converted to their cost, when
you add the
-B/--costflag (see Cost reporting).
Advantages of equity postings
- they help to keep the accounting equation balanced (if you care about that)
- they translate easily to any other double entry accounting system.
Most hledger users use cost notation and don't use equity postings.
But you can always convert cost notation to equity postings by adding
--infer-equity. Eg try hledger print -x --infer-equity.
And you can usually convert equity postings to cost notation by adding
--infer-costs (see Requirements for detecting equity conversion
postings). Eg
try hledger print -x --infer-costs.
Finally: using both equity postings and cost notation at the same time is allowed, as long as the journal entry is well formed such that the equity postings / cost equivalences can be detected. (Otherwise you'll get an error message saying that the transaction is unbalanced.):
2026-01-01 buy euros
assets:dollars $-123
equity:conversion $123
equity:conversion €-100
assets:euros €100 @ $1.23
So in principle you could enable both --infer-equity and
--infer-costs in your config file, and your reports would have the
advantages of both.
Cost basis / lot syntax
If you are buying some commodity to hold as an investment, it may be important to keep track of
- its original acquisition cost
- its original acquisition date
- and a sequence number or label, if needed, to disambiguate multiple acquisitions on the same day, or to serve as a mnemonic for easy reference.
In hledger we call these three the "cost basis"; and if an amount being acquired has a cost basis, we call it a "lot". Tax authorities often require that lots are tracked carefully and disposed of (sold) in a certain order.
Note, though "cost basis" sounds similar to the "cost" (transacted price) discussed above, they are distinct concepts. In some transactions the transacted price and basis cost are the same, but in others they are not.
So cost basis has its own syntax, also called "lot syntax". hledger's lot syntax is like Ledger's: one or more of the following annotations, following the main amount:
{LOTUNITCOST}or{{LOTTOTALCOST}}(see lot price)[LOTDATE](see lot date)(LOTLABEL)(see lot note)
hledger does not yet do anything with this lot syntax, except to
preserve it and show it in print's txt, beancount, and json
output. This means you can use this syntax in your hledger journals
(plus an amountless extra posting to help transactions balance, if
needed), then use the print command to export to Ledger or Beancount
or rustledger, to use their lots/gains reports (see Export Lots
workflow).
Balance assertions
hledger supports Ledger-style balance
assertions
in journal files. These look like, for example, = EXPECTEDBALANCE
following a posting's amount. Eg here we assert the expected dollar
balance in accounts a and b after each posting:
2013/1/1
a $1 = $1
b = $-1
2013/1/2
a $1 = $2
b $-1 = $-2
After reading a journal file, hledger will check all balance assertions
and report an error if any of them fail. Balance assertions can protect
you from, eg, inadvertently disrupting reconciled balances while
cleaning up old entries. You can disable them temporarily with the
-I/--ignore-assertions flag, which can be useful for troubleshooting
or for reading Ledger files. (Note: this flag currently does not disable
balance assignments, described below).
Assertions and ordering
hledger calculates and checks an account's balance assertions in date order (and when there are multiple assertions on the same day, in parse order). Note this is different from Ledger, which checks assertions always in parse order, ignoring dates.
This means in hledger you can freely reorder transactions, postings, or files, and balance assertions will usually keep working. The exception is when you reorder multiple postings on the same day, to the same account, which have balance assertions; those will likely need updating.
Assertions and multiple files
If an account has transactions appearing in multiple files, balance assertions can still work - but only if those files are part of a hierarchy made by include directives.
If the same files are specified with two -f options on the command
line, the assertions in the second will not see the balances from the
first.
To work around this, arrange your files in a hierarchy with include.
Or, you could concatenate the files temporarily, and process them like
one big file.
Why does it work this way ? It might be related to hledger's goal of stable predictable reports. File hierarchy is considered "permanent", part of your data, while the order of command line options/arguments is not. We don't want transient changes to be able to change the meaning of the data. Eg it would be frustrating if tomorrow all your balance assertions broke because you wrote command line arguments in a different order. (Discussion welcome.)
Assertions and costs
Balance assertions ignore costs, and should normally be written without one:
2019/1/1
(a) $1 @ €1 = $1
We do allow costs to be written in balance assertion amounts, however, and print shows them, but they don't affect whether the assertion passes or fails. This is for backward compatibility (hledger's close command used to generate balance assertions with costs), and because balance assignments do use costs (see below).
Assertions and commodities
The balance assertions described so far are "single commodity balance assertions": they assert and check the balance in one commodity, ignoring any others that may be present. This is how balance assertions work in Ledger also.
If an account contains multiple commodities, you can assert their balances by writing multiple postings with balance assertions, one for each commodity:
2013/1/1
usd $-1
eur €-1
both
2013/1/2
both 0 = $1
both 0 = €1
In hledger you can make a stronger "sole commodity balance
assertion" by writing two equals signs (== EXPECTEDBALANCE). This
also asserts that there are no other commodities in the account besides
the asserted one (or at least, that their current balance is zero):
2013/1/1
usd $-1 == $-1 ; these sole commodity assertions succeed
eur €-1 == €-1
both ;== $1 ; this one would fail because 'both' contains $ and €
It's less easy to make a "sole commodities balance assertion" (note the plural) - ie, asserting that an account contains two or more specified commodities and no others. It can be done by
- isolating each commodity in a subaccount, and asserting those
- and also asserting there are no commodities in the parent account itself:
2013/1/1
usd $-1
eur €-1
both 0 == 0 ; nothing up my sleeve
both:usd $1 == $1 ; a dollar here
both:eur €1 == €1 ; a euro there
Assertions and subaccounts
All of the balance assertions above (both = and ==) are
"subaccount-exclusive balance assertions"; they ignore any
balances that exist in deeper subaccounts.
In hledger you can make "subaccount-inclusive balance assertions"
by adding a star after the equals (=* or ==*):
2019/1/1
equity:start
assets:checking $10
assets:savings $10
assets $0 ==* $20 ; assets + subaccounts contains $20 and nothing else
Assertions and status
Balance assertions always consider postings of all statuses
(unmarked, pending, or cleared); they are not affected by the
-U/--unmarked / -P/--pending / -C/--cleared flags or the
status: query.
Assertions and virtual postings
Balance assertions always consider both real and
virtual postings; they are not affected by the
--real/-R flag or real: query.
Assertions and auto postings
Balance assertions are affected by the --auto flag, which generates
auto postings, which can alter account balances.
Because auto postings are optional in hledger, accounts affected by them
effectively have two balances. But balance assertions can only test one
or the other of these. So to avoid making fragile assertions, either:
- assert the balance calculated with
--auto, and always use--autowith that file - or assert the balance calculated without
--auto, and never use--autowith that file - or avoid balance assertions on accounts affected by auto postings (or avoid auto postings entirely).
Assertions and precision
Balance assertions compare the exactly calculated amounts, which are not always what is shown by reports. Eg a commodity directive may limit the display precision, but this will not affect balance assertions. Balance assertion failure messages show exact amounts.
Assertions and hledger add
Balance assertions can be included in the amounts given in add. All
types of assertions are supported, and assertions can be used as in a
normal journal file.
All transactions, not just those that have an explicit assertion, are validated against the existing assertions in the journal. This means it is possible for an added transaction to fail even if its assertions are correct as of the transaction date.
If this assertion checking is not desired, then it can be disabled with
-I.
However, balance assignments are currently not supported.
Posting comments
Text following ;, at the end of a posting line, and/or on indented
lines immediately below it, form comments for that posting. They are
reproduced by print but otherwise ignored, except they may contain
tags, which are not ignored.
2012-01-01
expenses 1 ; a comment for posting 1
assets
; a comment for posting 2
; a second comment line for posting 2
Transaction balancing
How exactly does hledger decide when a transaction is balanced ? Especially when it involves costs, which often are not exact, because of repeating decimals, or imperfect data from financial institutions ? In each commodity, hledger sums the transaction's posting amounts, after converting any with costs; then it checks if that sum is zero, when rounded to a suitable number of decimal digits - which we call the balancing precision.
Since version 1.50, hledger infers balancing precision in each
transaction from the amounts in that transaction's journal entry (like
Ledger). Ie, when checking the balance of commodity A, it uses the
highest decimal precision seen for A in the journal entry (excluding
cost amounts). This makes transaction balancing robust; any imbalances
must be visibly accounted for in the journal entry, display precision
can be freely increased with -c, and compatibility with Ledger and
Beancount journals is good.
Note that hledger versions before 1.50 worked differently: they allowed display precision to override the balancing precision. This masked small imbalances and caused fragility (see issue #2402). As a result, some journal entries (or CSV rules) that worked with hledger <1.50, are now rejected with an "unbalanced transaction" error. If you hit this problem, it's easy to fix:
-
You can restore the old behaviour, by adding
--txn-balancing=oldto the command or to your~/.hledger.conffile. This lets you keep using old journals unchanged, though without the above benefits. -
Or you can fix the problem entries (recommended). There are three ways, use whichever seems best:
- make cost amounts more precise (add more/better decimal digits)
- or make non-cost amounts less precise (remove unnecessary decimal digits that are raising the precision)
- or add a posting to absorb the imbalance (eg "expenses:rounding". Remember that one posting may omit the amount; that's convenient here.)
Tags
Tags are a way to add extra labels or data fields to transactions,
postings, or accounts, which you can match with a tag: query in
reports. (See queries below.)
Tags are a single word or hyphenated word, immediately followed by a full colon, written within a comment. (Yes, storing data in comments is slightly weird.) Here's a transaction with a tag:
2025-01-01 groceries ; some-tag:
assets:checking
expenses:food $1
A tag can have a value, a single line of text written after the colon. Tag values can't contain newlines.:
2025-01-01 groceries ; tag1: this is tag1's value
Multiple tags can be separated by comma. Tag values can't contain commas.:
2025-01-01 groceries ; tag1:value 1, tag2:value 2, comment text
A tag can have multiple values:
2025-01-01 groceries ; tag1:value 1, tag1:value 2
You can write each tag on its own line of you prefer (but they still can't contain commas):
2025-01-01 groceries
; tag1: value 1
; tag2: value 2
Tags can be attached to individual postings, rather than the overall transaction:
2025-01-01 rent
assets:checking
expenses:rent $1000 ; postingtag:
Tags can be attached to accounts, in their account directive:
account assets:checking ; acct-number: 123-45-6789
Tag propagation
In addition to what they are attached to, tags also affect related data in a few ways, allowing more powerful queries:
- Accounts -> postings. Postings inherit tags from their account.
- Transactions -> postings. Postings inherit tags from their transaction.
- Postings -> transactions. Transactions also acquire the tags of their postings.
So when you use a tag: query to match whole
transactions, individual postings, or accounts, it's good to understand
how tags behave. Here's an example showing all three kinds of
propagation:
account assets:checking
account expenses:food ; atag:
2025-01-01 groceries ; ttag:
assets:checking ; p1tag:
expenses:food $1 ; p2tag:
| data part | has tags | explanation |
|---|---|---|
| assets:checking account | no tags attached | |
| expenses:food account | atag | atag: in comment |
| assets:checking posting | p1tag, ttag | p1tag: in comment, ttag acquired from transaction |
| expenses:food posting | p2tag, atag, ttag | p2tag: in comment, atag from account, ttag from transaction |
| groceries transaction | ttag, p1tag, p2tag, atag | ttag: in comment, p1tag from first posting, p2tag and atag from second posting |
Displaying tags
You can use the tags command to list tag names or values.
The print command also shows tags.
You can use --pivot to display tag values in other reports, in various ways (eg appended to account names, like pseudo subaccounts).
When to use tags ?
Tags provide more dimensions of categorisation, complementing accounts
and transaction descriptions. When to use each of these is somewhat a
matter of taste. Accounts have the most built-in support, and regex
queries on descriptions are also quite powerful. So you may not need
tags at all. But if you want to track multiple cross-cutting categories,
they can be a good fit. For example, you could tag trip-related
transactions with trip: YEAR:PLACE, without disturbing your usual
account categories.
Tag names
What is allowed in a tag name ? Most non-whitespace characters. Eg 😀:
is a valid tag.
For extra error checking, you can declare valid tag names with the
tag directive, and then enforce these with the
check command. But note that tags are detected quite loosely
at present, sometimes where you didn't intend them. Eg a comment like
; see https://foo.com adds a https tag.
There are several tag names which have special significance to hledger. They are explained elsewhere, but here's a quick reference:
type -- declares an account's type
date -- overrides a posting's date
date2 -- overrides a posting's secondary date
assert -- appears on txns generated by close --assert
retain -- appears on txns generated by close --retain
start -- appears on txns generated by close --migrate/--close/--open/--assign
t -- appears on postings generated from timedot letters
generated-transaction -- appears on txns generated by a periodic rule
modified-transaction -- appears on txns which have had auto postings added
generated-posting -- appears on generated postings
cost-posting -- appears on postings which have (or could have) a cost,
and which have equivalent conversion postings in the transaction
conversion-posting -- appears on postings which are to a V/Conversion account
and which have an equivalent cost posting in the transaction
The second group above (generated-transaction, etc.) are normally
hidden, with a _ prefix added. This means print doesn't show them
by default; but you can still use them in queries. You can add the
--verbose-tags flag to make them visible in print output, which can
be useful for troubleshooting.
Directives
Besides transactions, there is something else you can put in a journal
file: directives. These are declarations, beginning with a keyword, that
modify hledger's behaviour. Some directives can have more specific
subdirectives, indented below them. hledger's directives are similar to
Ledger's in many cases, but there are also many
differences. Directives are not required, but can be
useful. Here are the main directives:
| purpose | directive |
|---|---|
| READING DATA: | |
| Rewrite account names | alias |
| Comment out sections of the file | comment |
| Declare file's decimal mark, to help parse amounts accurately | decimal-mark |
| Include other data files | include |
| GENERATING DATA: | |
| Generate recurring transactions or budget goals | ~ |
| Generate extra postings on existing transactions | = |
| CHECKING FOR ERRORS: | |
| Define valid entities to provide more error checking | account, commodity, payee, tag |
| REPORTING: | |
| Declare accounts' type and display order | account |
| Declare commodity display styles | commodity |
| Declare market prices | P |
Directives and multiple files
Directives vary in their scope, ie which journal entries and which input
files they affect. Most often, a directive will affect the following
entries and included files if any, until the end of the current file -
and no further. You might find this inconvenient! For example, alias
directives do not affect parent or sibling
files. But there are usually workarounds;
for example, put alias directives in your top-most file, before
including other files.
The restriction, though it may be annoying at first, is in a good cause; it allows reports to be stable and deterministic, independent of the order of input. Without it, reports could show different numbers depending on the order of -f options, or the positions of include directives in your files.
Directive effects
Here are all hledger's directives, with their effects and scope summarised - nine main directives, plus four others which we consider non-essential:
| directive | what it does | ends at file end? |
|---|---|---|
account | Declares an account, for checking all entries in all files; and its display order and type. Subdirectives: any text, ignored. | N |
alias | Rewrites account names, in following entries until end of current file or end aliases. Command line equivalent: --alias | Y |
comment | Ignores part of the journal file, until end of current file or end comment. | Y |
commodity | Declares up to four things: 1. a commodity symbol, for checking all amounts in all files 2. the display style for all amounts of this commodity 3. the decimal mark for parsing amounts of this commodity, in the rest of this file and its children, if there is no decimal-mark directive 4. the precision to use for balanced-transaction checking in this commodity, in this file and its children. Takes precedence over D. Subdirectives: format (ignored). Command line equivalent: -c/--commodity-style | N, N, Y, Y |
decimal-mark | Declares the decimal mark, for parsing amounts of all commodities in following entries until next decimal-mark or end of current file. Included files can override. Takes precedence over commodity and D. | Y |
include | Includes entries and directives from another file, as if they were written inline. Command line alternative: multiple -f/--file | N |
payee | Declares a payee name, for checking all entries in all files. | N |
P | Declares the market price of a commodity on some date, for value reports. | N |
~ (tilde) | Declares a periodic transaction rule that generates future transactions with --forecast and budget goals with balance --budget. | N |
| Other syntax: | ||
apply account | Prepends a common parent account to all account names, in following entries until end of current file or end apply account. | Y |
D | Sets a default commodity to use for no-symbol amounts; and, if there is no commodity directive for this commodity: its decimal mark, balancing precision, and display style, as above. | Y, Y, N, N |
Y | Sets a default year to use for any yearless dates, in following entries until end of current file. | Y |
= (equals) | Declares an auto posting rule that generates extra postings on matched transactions with --auto, in current, parent, and child files (but not sibling files, see #1212). | partly |
| Other Ledger directives | Other directives from Ledger's file format are accepted but ignored. |
account directive
account directives can be used to declare accounts (ie, the places
that amounts are transferred from and to). Though not required, these
declarations can provide several benefits:
- They can document your intended chart of accounts, providing a reference.
- They can store additional account information as comments, or as tags which can be used to filter or pivot reports.
- They can restrict which accounts may be posted to by transactions, eg in strict mode, which helps prevent errors.
- They influence account display order in reports, allowing non-alphabetic sorting (eg Revenues to appear above Expenses).
- They can help hledger know your accounts' types (asset, liability, equity, revenue, expense), enabling reports like balancesheet and incomestatement.
- They help with account name completion (in hledger add, hledger-web, hledger-iadd, ledger-mode, etc.)
They are written as the word account followed by a hledger-style
account name. Eg:
account assets:bank:checking
Ledger-style indented subdirectives are also accepted, but ignored:
account assets:bank:checking
format subdirective ; currently ignored
Account comments
Text following two or more spaces and ; at the end of an account
directive line, and/or following ; on indented lines immediately below
it, form comments for that account.
Same-line account comments require two+ spaces before ; because that
character can appear in account names.
account assets:bank:checking ; same-line comment, at least 2 spaces before the semicolon
; next-line comment
; some tags - type:A, acctnum:12345
Account tags
An account directive's comment may contain tags. These will be propagated to all postings using that account, as hidden but queryable posting tags, except where the posting already a tag of the same name. (Posting tags override account tags.)
Account error checking
By default, accounts need not be declared; they come into existence when a posting references them. This is convenient, but it means hledger can't warn you when you mis-spell an account name in the journal. Usually you'll find that error later, as an extra account in balance reports, or an incorrect balance when reconciling.
In strict mode, enabled with the -s/--strict flag,
or when you run hledger check accounts, hledger will report an error
if any transaction uses an account name that has not been declared by an
account directive. Some notes:
- The declaration is case-sensitive; transactions must use the correct account name capitalisation.
- The account directive's scope is "whole file and below" (see directives). This means it affects all of the current file, and any files it includes, but not parent or sibling files. The position of account directives within the file does not matter, though it's usual to put them at the top.
- Accounts can only be declared in
journalfiles, but will affect included files of all types. - It's currently not possible to declare "all possible subaccounts" with a wildcard; every account posted to must be declared.
- If you use the --infer-equity flag, you will also need declarations for the account names it generates.
Account display order
Account directives also cause hledger to display accounts in a particular order, not just alphabetically. Eg, here is a conventional ordering for the top-level accounts:
account assets
account liabilities
account equity
account revenues
account expenses
Now hledger displays them in that order:
$ hledger accounts
assets
liabilities
equity
revenues
expenses
If there are undeclared accounts, those will be displayed last, in alphabetical order.
Sorting is done within each group of sibling accounts, at each level of
the account tree. Eg, a declaration like account parent:child
influences child's position among its siblings.
Note, it does not affect parent's position; for that, you need an
account parent declaration.
Sibling accounts are always displayed together; hledger won't display
x:y in between a:b and a:c.
An account directive both declares an account as a valid posting target, and declares its display order; you can't easily do one without the other.
Account types
hledger knows that in accounting there are three main account types:
Asset | A | things you own |
Liability | L | things you owe |
Equity | E | owner's investment, balances the two above |
and two more representing changes in these:
Revenue | R | inflows (also known as Income) |
Expense | X | outflows |
hledger also uses a few subtypes:
Cash | C | liquid assets (subtype of Asset) |
Conversion | V | commodity conversions equity (subtype of Equity) |
Gain | G | capital gains/losses (subtype of Revenue) |
As a convenience, hledger will detect most of these types automatically
from english account names. But it's better to declare them explicitly
by adding a type: tag in the account directives. The tag's
value can be any of the types or one-letter abbreviations above.
Here is a typical set of account type declarations. Subaccounts will inherit their parent's type, or can override it:
account assets ; type: A
account liabilities ; type: L
account equity ; type: E
account revenues ; type: R
account expenses ; type: X
account assets:bank ; type: C
account assets:cash ; type: C
account equity:conversion ; type: V
account revenues:capital ; type: G
This enables the easy balancesheet, balancesheetequity, cashflow and incomestatement reports, and querying by type:.
Tips:
-
You can list accounts and their types, for troubleshooting:
$ hledger accounts --types [ACCTPAT] [type:TYPECODES] [-DEPTH] [--locations] -
It's a good idea to declare at least one account for each account type. Having some types declared and some inferred can disrupt certain reports.
-
The rules for inferring types from account names are as follows (using Regular expressions).
If they don't work for you, just ignore them and declare your types withtype:tags.If account's name contains this case insensitive regular expression | its type is --------------------------------------------------------------------|------------- ^assets?(:.+)?:(cash|bank|che(ck|que?)(ing)?|savings?|current)(:|$) | Cash ^assets?(:|$) | Asset ^(debts?|liabilit(y|ies))(:|$) | Liability ^equity:(trad(e|ing)|conversion)s?(:|$) | Conversion ^equity(:|$) | Equity ^(income|revenue)s?(:|$) | Revenue ^expenses?(:|$) | Expense -
As mentioned above, subaccounts will inherit a type from their parent account. To be precise, an account's type is decided by the first of these that exists:
- A
type:declaration for this account. - A
type:declaration in the parent accounts above it, preferring the nearest. - An account type inferred from this account's name.
- An account type inferred from a parent account's name, preferring the nearest parent.
- Otherwise, it will have no type.
- A
-
Account aliases can disrupt account types.
alias directive
You can define account alias rules which rewrite your account names, or parts of them, before generating reports. This can be useful for:
- expanding shorthand account names to their full form, allowing easier data entry and a less verbose journal
- adapting old journals to your current chart of accounts
- experimenting with new account organisations, like a new hierarchy
- combining two accounts into one, eg to see their sum or difference on one line
- customising reports
Account aliases also rewrite account names in account directives. They do not affect account names being entered via hledger add or hledger-web.
Account aliases are very powerful. They are generally easy to use correctly, but you can also generate invalid account names with them; more on this below.
See also Rewrite account names.
Basic aliases
To set an account alias, use the alias directive in your journal file.
This affects all subsequent journal entries in the current file or its
included files (but note: not sibling or parent
files). The spaces around the = are
optional:
alias OLD = NEW
Or, you can use the --alias 'OLD=NEW' option on the command line. This
affects all entries. It's useful for trying out aliases interactively.
OLD and NEW are case sensitive full account names. hledger will replace any occurrence of the old account name with the new one. Subaccounts are also affected. Eg:
alias checking = assets:bank:wells fargo:checking
; rewrites "checking" to "assets:bank:wells fargo:checking", or "checking:a" to "assets:bank:wells fargo:checking:a"
Regex aliases
There is also a more powerful variant that uses a regular expression, indicated by wrapping the pattern in forward slashes. (This is the only place where hledger requires forward slashes around a regular expression.)
Eg:
alias /REGEX/ = REPLACEMENT
or:
$ hledger --alias '/REGEX/=REPLACEMENT' ...
Any part of an account name matched by REGEX will be replaced by REPLACEMENT. REGEX is case-insensitive as usual.
If you need to match a forward slash, escape it with a backslash, eg
/\/=:.
If REGEX contains parenthesised match groups, these can be referenced by the usual backslash and number in REPLACEMENT:
alias /^(.+):bank:([^:]+):(.*)/ = \1:\2 \3
; rewrites "assets:bank:wells fargo:checking" to "assets:wells fargo checking"
REPLACEMENT continues to the end of line (or on command line, to end of option argument), so it can contain trailing whitespace.
Combining aliases
You can define as many aliases as you like, using journal directives and/or command line options.
Recursive aliases - where an account name is rewritten by one alias, then by another alias, and so on - are allowed. Each alias sees the effect of previously applied aliases.
In such cases it can be important to understand which aliases will be applied and in which order. For (each account name in) each journal entry, we apply:
aliasdirectives preceding the journal entry, most recently parsed first (ie, reading upward from the journal entry, bottom to top)--aliasoptions, in the order they appeared on the command line (left to right).
In other words, for (an account name in) a given journal entry:
- the nearest alias declaration before/above the entry is applied first
- the next alias before/above that will be be applied next, and so on
- aliases defined after/below the entry do not affect it.
This gives nearby aliases precedence over distant ones, and helps provide semantic stability - aliases will keep working the same way independent of which files are being read and in which order.
In case of trouble, adding --debug=6 to the command line will show
which aliases are being applied when.
Aliases and multiple files
As explained at Directives and multiple
files, alias directives do not affect
parent or sibling files. Eg in this command,
hledger -f a.aliases -f b.journal
account aliases defined in a.aliases will not affect b.journal. Including the aliases doesn't work either:
include a.aliases
2023-01-01 ; not affected by a.aliases
foo 1
bar
This means that account aliases should usually be declared at the start of your top-most file, like this:
alias foo=Foo
alias bar=Bar
2023-01-01 ; affected by aliases above
foo 1
bar
include c.journal ; also affected
end aliases directive
You can clear (forget) all currently defined aliases (seen in the journal so far, or defined on the command line) with this directive:
end aliases
Aliases can generate bad account names
Be aware that account aliases can produce malformed account names, which
could cause confusing reports or invalid print output. For
example, you could erase all account names:
2021-01-01
a:aa 1
b
$ hledger print --alias '/.*/='
2021-01-01
1
The above print output is not a valid journal. Or you could insert an
illegal double space, causing print output that would give a different
journal when reparsed:
2021-01-01
old 1
other
$ hledger print --alias old="new USD" | hledger -f- print
2021-01-01
new USD 1
other
Aliases and account types
If an account with a type declaration (see Declaring accounts > Account types) is renamed by an alias, normally the account type remains in effect.
However, renaming in a way that reshapes the account tree (eg renaming parent accounts but not their children, or vice versa) could prevent child accounts from inheriting the account type of their parents.
Secondly, if an account's type is being inferred from its name, renaming it by an alias could prevent or alter that.
If you are using account aliases and the type: query is
not matching accounts as you expect, try troubleshooting with the
accounts command, eg something like:
$ hledger accounts --types -1 --alias assets=bassetts
commodity directive
The commodity directive performs several functions:
-
It declares which commodity symbols may be used in the journal, enabling useful error checking with strict mode or the check command. See Commodity error checking below.
-
It declares how all amounts in this commodity should be displayed, eg how many decimals to show. See Commodity display style above.
-
(If no
decimal-markdirective is in effect:) It sets the decimal mark to expect (period or comma) when parsing amounts in this commodity, in this file and files it includes, from the directive until end of current file. See Decimal marks above. -
It declares the precision with which this commodity's amounts should be compared when checking for balanced transactions, anywhere in this file and files it includes, until end of current file.
Declaring commodities solves several common parsing/display problems, so we recommend it.
Note that effects 3 and 4 above end at the end of the directive's file, and will not affect sibling or parent files. So if you are relying on them (especially 4) and using multiple files, placing your commodity directives in a top-level parent file might be important. Or, keep your decimal marks unambiguous and your entries well balanced and precise.
Omitting the commodity symbol will set the display style for just the no-symbol commodity, not all commodities.
Commodity styles can be overridden by the
-c/--commodity-style command line option.
(Related: #793)
Commodity directive syntax
A commodity directive is normally the word commodity followed by a
sample amount, and optionally a comment. Only the amount's
symbol and the number's format is significant. Eg:
commodity $1000.00
commodity 1.000,00 EUR
commodity 1 000 000.0000 ; the no-symbol commodity
A commodity directive's sample amount must always include a period or comma decimal mark (this rule helps disambiguate decimal marks and digit group marks). If you don't want to show any decimal digits, write the decimal mark at the end:
commodity 1000. AAAA ; show AAAA with no decimals
Commodity symbols containing spaces, numbers, or punctuation must be enclosed in double quotes, as usual:
commodity 1.0000 "AAAA 2023"
Commodity directives normally include a sample amount, but can declare only a symbol (ie, just function 1 above):
commodity $
commodity INR
commodity "AAAA 2023"
commodity "" ; the no-symbol commodity
Commodity directives may also be written with an indented format
subdirective, as in Ledger. The symbol is repeated and must be the same
in both places. Other subdirectives are currently ignored:
; display indian rupees with currency name on the left,
; thousands, lakhs and crores comma-separated,
; period as decimal point, and two decimal places.
commodity INR
format INR 1,00,00,000.00
an unsupported subdirective ; ignored by hledger
Commodity tags
A commodity directive's comment may contain tags. These will be propagated to all postings using that commodity in their main amount, as hidden but queryable posting tags, except where the posting already a tag of the same name. (Posting tags override account tags override commodity tags.)
Commodity error checking
In strict mode (-s/--strict) (or when you run
hledger check commodities), hledger will report an error if an
undeclared commodity symbol is used. (With one exception: zero amounts
are always allowed to have no commodity symbol.) It works like account
error checking (described above).
decimal-mark directive
You can use a decimal-mark directive - usually one per file, at the
top of the file - to declare which character represents a decimal mark
when parsing amounts in this file. It can look like
decimal-mark .
or
decimal-mark ,
This prevents any ambiguity when parsing numbers in the file, so we recommend it, especially if the file contains digit group marks (eg thousands separators).
include directive
You can pull in the content of additional files by writing an include directive, like this:
include SOMEFILE
This has the same effect as if SOMEFILE's content was inlined at this point. (With any include directives in SOMEFILE processed similarly, recursively.)
Only journal files can include other files. They can include journal, timeclock or timedot files, but not CSV files.
If the file path begins with a tilde, that means your home directory:
include ~/main.journal.
If it begins with a slash, it is an absolute path:
include /home/user/main.journal. Otherwise it is relative to the
including file's folder: include ../finances/main.journal.
Also, the path may have a file type prefix to force a specific file
format, overriding the file extension(s) (as described in Data
formats): include timedot:notes/2023.md.
The path may contain glob
patterns to match
multiple files. hledger's globs are similar to zsh's: ? to match any
character; [a-z] to match any character in a range; * to match zero
or more characters that aren't a path separator (like /); ** to
match zero or more subdirectories and/or zero or more characters at the
start of a file name; etc. For convenience, include always excludes
the current file. So, you can do
include *.journalto include all other journal files in the current directory (excluding dot files)include **.journalto include all other journal files in this directory and below (excluding dot files and top-level dot directories)include timelogs/2???.timedotto include all timedot files named like a year number.
Note * and ** usually won't match dot files or dot directories,
with one exception: ** does search non-top-level dot directories. If
this causes problems, make your glob pattern more specific (eg
**.journal instead of **).
If you are using many, or deeply nested, include files, and have an
error that's hard to pinpoint: a good troubleshooting command is
hledger files --debug=6 (or 7).
P directive
The P directive declares a market price, which is a conversion rate
between two commodities on a certain date. This allows value
reports to convert amounts of one commodity to their
value in another, on or after that date. These prices are often obtained
from a stock exchange,
cryptocurrency
exchange, or the
foreign exchange
market.
The format is:
P DATE COMMODITY1SYMBOL COMMODITY2AMOUNT
DATE is a simple date, COMMODITY1SYMBOL is the symbol of the commodity being priced, and COMMODITY2AMOUNT is the amount (symbol and quantity) of commodity 2 that one unit of commodity 1 is worth on this date. Examples:
# one euro was worth $1.35 from 2009-01-01 onward:
P 2009-01-01 € $1.35
# and $1.40 from 2010-01-01 onward:
P 2010-01-01 € $1.40
The -V, -X and --value flags use these market prices to show
amount values in another commodity. See Value
reporting.
payee directive
payee PAYEE NAME
This directive can be used to declare a limited set of payees which may appear in transaction descriptions. The "payees" check will report an error if any transaction refers to a payee that has not been declared. Eg:
payee Whole Foods ; a comment
Payees do not have tags (tags in the comment will be ignored).
To declare the empty payee name, use "".
payee ""
Ledger-style indented subdirectives, if any, are currently ignored.
tag directive
tag TAGNAME
This directive can be used to declare a limited set of tag names allowed in tags. TAGNAME should be a valid tag name (no spaces). Eg:
tag item-id
Any indented subdirectives are currently ignored.
The "tags" check will report an error if any undeclared tag name is used. It is quite easy to accidentally create a tag through normal use of colons in comments; if you want to prevent this, you can declare and check your tags .
Periodic transactions
The ~ directive declares a "periodic rule" which generates temporary
extra transactions, usually recurring at some interval, when hledger is
run with the --forecast flag. These "forecast transactions" are
useful for forecasting future activity. They exist only
for the duration of the report, and only when --forecast is used; they
are not saved in the journal file by hledger.
Periodic rules also have a second use: with the --budget flag they set
budget goals for budgeting.
Periodic rules can be a little tricky, so before you use them, read this whole section, or at least the following tips:
- Two spaces accidentally added or omitted will cause you trouble - read about this below.
- For troubleshooting, show the generated transactions with
hledger print --forecast tag:generatedorhledger register --forecast tag:generated. - Forecasted transactions will begin only after the last non-forecasted transaction's date.
- Forecasted transactions will end 6 months from today, by default. See below for the exact start/end rules.
- period expressions can be tricky. Their documentation needs improvement, but is worth studying.
- Some period expressions with a repeating interval must begin on a
natural boundary of that interval. Eg in
weekly from DATE, DATE must be a monday.~ weekly from 2019/10/1(a tuesday) will give an error. - Other period expressions with an interval are automatically expanded
to cover a whole number of that interval. (This is done to improve
reports, but it also affects periodic transactions. Yes, it's a bit
inconsistent with the above.) Eg:
~ every 10th day of month from 2023/01, which is equivalent to
~ every 10th day of month from 2023/01/01, will be adjusted to start on 2019/12/10.
Periodic rule syntax
A periodic transaction rule looks like a normal journal entry, with the
date replaced by a tilde (~) followed by a period
expression (mnemonic: ~ looks like a recurring
sine wave.):
# every first of month
~ monthly
expenses:rent $2000
assets:bank:checking
# every 15th of month in 2023's first quarter:
~ monthly from 2023-04-15 to 2023-06-16
expenses:utilities $400
assets:bank:checking
The period expression is the same syntax used for specifying multi-period reports, just interpreted differently; there, it specifies report periods; here it specifies recurrence dates (the periods' start dates).
Periodic rules and relative dates
Partial or relative dates (like 12/31, 25, tomorrow, last week,
next quarter) are usually not recommended in periodic rules, since the
results will change as time passes. If used, they will be interpreted
relative to, in order of preference:
- the first day of the default year specified by a recent
Ydirective - or the date specified with
--today - or the date on which you are running the report.
They will not be affected at all by report period or forecast period dates.
Two spaces between period expression and description!
If the period expression is followed by a transaction description, these must be separated by two or more spaces. This helps hledger know where the period expression ends, so that descriptions can not accidentally alter their meaning, as in this example:
; 2 or more spaces needed here, so the period is not understood as "every 2 months in 2023"
; ||
; vv
~ every 2 months in 2023, we will review
assets:bank:checking $1500
income:acme inc
So,
- Do write two spaces between your period expression and your transaction description, if any.
- Don't accidentally write two spaces in the middle of your period expression.
Auto postings
The = directive declares an "auto posting rule", which adds extra
postings to existing transactions. (Remember, postings are
the account name & amount lines below a transaction's date &
description.)
In the journal, an auto posting rule looks quite like a transaction, but
instead of date and description it has = (mnemonic: "match") and a
query, like this:
= QUERY
ACCOUNT AMOUNT
...
Queries are just like command line queries; an account name substring is most common. Query terms containing spaces should be enclosed in single or double quotes.
Each = rule works like this: when hledger is run with the --auto
flag, wherever the QUERY matches a posting in the journal, the rule's
postings are added to that transaction, immediately below the matched
posting. Note these generated postings are temporary, existing only for
the duration of the report, and only when --auto is used; they are not
saved in the journal file by hledger.
The postings can contain the special string %account which will be
expanded to the account name of the matched account.
Generated postings' amounts can depend on the matched posting's amount. So auto postings can be useful for, eg, adding tax postings with a standard percentage. AMOUNT can be:
-
a number with no commodity symbol, like
2. The matched posting's commodity symbol will be added to this. -
a normal amount with a commodity symbol, like
$2. This will be used as-is. -
an asterisk followed by a number, like
*2. This will multiply the matched posting's amount (and total price, if any) by the number. -
an asterisk followed by an amount with commodity symbol, like
*$2. This multiplies and also replaces the commodity symbol with this new one.
Some examples:
; every time I buy food, schedule a dollar donation
= expenses:food
(liabilities:charity) $-1
; when I buy a gift, also deduct that amount from a budget envelope subaccount
= expenses:gifts
assets:checking:gifts *-1
assets:checking *1
2017/12/1
expenses:food $10
assets:checking
2017/12/14
expenses:gifts $20
assets:checking
$ hledger print --auto
2017-12-01
expenses:food $10
assets:checking
(liabilities:charity) $-1
2017-12-14
expenses:gifts $20
assets:checking
assets:checking:gifts -$20
assets:checking $20
Note that depending fully on generated data such as this has some
drawbacks - it's less portable, less future-proof, less auditable by
others, and less robust (eg your balance assertions will depend on
whether you use or don't use --auto). An alternative is to use auto
postings in "one time" fashion - use them to help build a complex
journal entry, view it with hledger print --auto, and then copy that
output into the journal file to make it permanent.
Auto postings and multiple files
An auto posting rule can affect any transaction in the current file, or
in any parent file or child file. Note, currently it will not affect
sibling files (when multiple -f/--file are used - see
#1212).
Auto postings and dates
A posting date (or secondary date) in the matched posting, or (taking precedence) a posting date in the auto posting rule itself, will also be used in the generated posting.
Auto postings and transaction balancing / inferred amounts / balance assertions
Currently, auto postings are added:
- after missing amounts are inferred, and transactions are checked for balancedness,
- but before balance assertions are checked.
Note this means that journal entries must be balanced both before and after auto postings are added. This changed in hledger 1.12+; see #893 for background.
This also means that you cannot have more than one auto-posting with a missing amount applied to a given transaction, as it will be unable to infer amounts.
Auto posting tags
Automated postings will have some extra tags:
generated-posting:= QUERY- shows this was generated by an auto posting rule, and the query_generated-posting:= QUERY- a hidden tag, which does not appear in hledger's output. This can be used to match postings generated "just now", rather than generated in the past and saved to the journal.
Also, any transaction that has been changed by auto posting rules will have these tags added:
modified:- this transaction was modified_modified:- a hidden tag not appearing in the comment; this transaction was modified "just now".
Auto postings on forecast transactions only
Tip: you can can make auto postings that will apply to forecast
transactions but not recorded transactions, by adding
tag:_generated-transaction to their QUERY. This can be useful when
generating new journal entries to be saved in the journal.
Other syntax
hledger journal format supports quite a few other features, mainly to make interoperating with or converting from Ledger easier. Note some of the features below are powerful and can be useful in special cases, but in general, features in this section are considered less important or even not recommended for most users. Downsides are mentioned to help you decide if you want to use them.
Balance assignments
Ledger-style balance assignments are also supported. These are like balance assertions, but with no posting amount on the left side of the equals sign; instead it is calculated automatically so as to satisfy the assertion. This can be a convenience during data entry, eg when setting opening balances:
; starting a new journal, set asset account balances
2016/1/1 opening balances
assets:checking = $409.32
assets:savings = $735.24
assets:cash = $42
equity:opening balances
or when adjusting a balance to reality:
; no cash left; update balance, record any untracked spending as a generic expense
2016/1/15
assets:cash = $0
expenses:misc
The calculated amount depends on the account's balance in the commodity at that point (which depends on the previously-dated postings of the commodity to that account since the last balance assertion or assignment).
Downsides: using balance assignments makes your journal less explicit; to know the exact amount posted, you have to run hledger or do the calculations yourself, instead of just reading it. Also balance assignments' forcing of balances can hide errors. These things make your financial data less portable, less future-proof, and less trustworthy in an audit.
Balance assignments and costs
A cost in a balance assignment will cause the calculated amount to have that cost attached:
2019/1/1
(a) = $1 @ €2
$ hledger print --explicit
2019-01-01
(a) $1 @ €2 = $1 @ €2
Balance assignments and multiple files
Balance assignments handle multiple files like balance assertions. They see balance from other files previously included from the current file, but not from previous sibling or parent files.
Bracketed posting dates
For setting posting dates and secondary posting
dates, Ledger's bracketed date syntax is also
supported: [DATE], [DATE=DATE2] or [=DATE2] in posting comments.
hledger will attempt to parse any square-bracketed sequence of the
0123456789/-.= characters in this way. With this syntax, DATE infers
its year from the transaction and DATE2 infers its year from DATE.
Downsides: another syntax to learn, redundant with hledger's
date:/date2: tags, and confusingly similar to Ledger's lot date
syntax.
D directive
D AMOUNT
This directive sets a default commodity, to be used for any subsequent
commodityless amounts (ie, plain numbers) seen while parsing the
journal. This effect lasts until the next D directive, or the end of
the current file.
For compatibility/historical reasons, D also acts like a commodity
directive (setting the commodity's decimal mark
for parsing and display style for output). So
its argument is not just a commodity symbol, but a full amount
demonstrating the style. The amount must include a decimal mark (either
period or comma). Eg:
; commodity-less amounts should be treated as dollars
; (and displayed with the dollar sign on the left, thousands separators and two decimal places)
D $1,000.00
1/1
a 5 ; <- commodity-less amount, parsed as $5 and displayed as $5.00
b
Interactions with other directives:
For setting a commodity's display style, a commodity directive has
highest priority, then a D directive.
For detecting a commodity's decimal mark during parsing, decimal-mark
has highest priority, then commodity, then D.
For checking commodity symbols with the check command, a
commodity directive is required (hledger check commodities ignores
D directives).
Downsides: omitting commodity symbols makes your financial data less
explicit, less portable, and less trustworthy in an audit. It is usually
an unsustainable shortcut; sooner or later you will want to track
multiple commodities. D is overloaded with functions redundant with
commodity and decimal-mark. And it works differently from Ledger's
D.
apply account directive
This directive sets a default parent account, which will be prepended to
all accounts in following entries, until an end apply account
directive or end of current file. Eg:
apply account home
2010/1/1
food $10
cash
end apply account
is equivalent to:
2010/01/01
home:food $10
home:cash $-10
account directives are also affected, and so is any included
content.
Account names entered via hledger add or hledger-web are not affected.
Account aliases, if any, are applied after the parent account is prepended.
Downsides: this can make your financial data less explicit, less portable, and less trustworthy in an audit.
Y directive
Y YEAR
or (deprecated backward-compatible forms):
year YEAR apply year YEAR
The space is optional. This sets a default year to be used for subsequent dates which don't specify a year. Eg:
Y2009 ; set default year to 2009
12/15 ; equivalent to 2009/12/15
expenses 1
assets
year 2010 ; change default year to 2010
2009/1/30 ; specifies the year, not affected
expenses 1
assets
1/31 ; equivalent to 2010/1/31
expenses 1
assets
Downsides: omitting the year (from primary transaction dates, at least) makes your financial data less explicit, less portable, and less trustworthy in an audit. Such dates can get separated from their corresponding Y directive, eg when evaluating a region of the journal in your editor. A missing Y directive makes reports dependent on today's date.
Secondary dates
A secondary date is written after the primary date, following an equals
sign: DATE1=DATE2. If the year is omitted, the primary date's year is
assumed. When running reports, the primary (left side) date is used by
default, but with the --date2 flag (--aux-date or--effective also
work, for Ledger users), the secondary (right side) date will be used
instead.
The meaning of secondary dates is up to you. Eg it could be "primary is the bank's clearing date, secondary is the date the transaction was initiated, if different".
In practice, this feature usually adds confusion:
- You have to remember the primary and secondary dates' meaning, and follow that consistently.
- It splits your bookkeeping into two modes, and you have to remember which mode is appropriate for a given report.
- Usually your balance assertions will work with only one of these modes.
- It makes your financial data more complicated, less portable, and less clear in an audit.
- It interacts with every feature, creating an ongoing cost for implementors.
- It distracts new users and supporters.
- Posting dates are simpler and work better.
So secondary dates are officially deprecated in hledger, remaining only as a Ledger compatibility aid; we recommend using posting dates instead.
Star comments
Lines beginning with * (star/asterisk) are also comment lines. This
feature allows Emacs users to insert org headings in their journal,
allowing them to fold/unfold/navigate it like an outline when viewed
with org mode.
Downsides: another, unconventional comment syntax to learn. Decreases your journal's portability. And switching to Emacs org mode just for folding/unfolding meant losing the benefits of ledger mode; nowadays you can add outshine mode to ledger mode to get folding without losing ledger mode's features.
Valuation expressions
Ledger allows a valuation function or value to be written in double parentheses after an amount. hledger ignores these.
Virtual postings
A posting with parentheses around the account name, like
(some:account) 10, is called an unbalanced virtual posting. These
postings do not participate in transaction balancing. (And if you write
them without an amount, a zero amount is always inferred.) These can
occasionally be convenient for special circumstances, but they violate
double entry bookkeeping and make your data less portable across
applications, so many people avoid using them at all.
A posting with brackets around the account name ([some:account]) is
called a balanced virtual posting. The balanced virtual postings in a
transaction must add up to zero, just like ordinary postings, but
separately from them. These are not part of double entry bookkeeping
either, but they are at least balanced. An example:
2022-01-01 buy food with cash, update budget envelope subaccounts, & something else
assets:cash $-10 ; <- these balance each other
expenses:food $7 ; <-
expenses:food $3 ; <-
[assets:checking:budget:food] $-10 ; <- and these balance each other
[assets:checking:available] $10 ; <-
(something:else) $5 ; <- this is not required to balance
Ordinary postings, whose account names are neither parenthesised nor
bracketed, are called real postings. You can exclude virtual postings
from reports with the -R/--real flag or a real:1 query.
Other Ledger directives
These other Ledger directives are currently accepted but ignored. This allows hledger to read more Ledger files, but be aware that hledger's reports may differ from Ledger's if you use these.
apply fixed COMM AMT
apply tag TAG
assert EXPR
bucket / A ACCT
capture ACCT REGEX
check EXPR
define VAR=EXPR
end apply fixed
end apply tag
end apply year
end tag
eval / expr EXPR
python
PYTHONCODE
tag NAME
value EXPR
--command-line-flags
See also https://hledger.org/ledger.html for a detailed hledger/Ledger syntax comparison.
Ledger virtual costs
In Ledger, (@) UNITCOST and (@@) TOTALCOST are virtual
costs,
which do not generate market prices. In hledger, these are equivalent to
@ and @@.
Ledger lot syntax
In Ledger, these optional annotations after an amount help specify the cost basis of a newly acquired lot, or select existing lot(s) to dispose of:
hledger does not yet calculate lots itself, but it accepts these
annotations and will show them in print's txt, beancount, and
json output formats. This means you can use this syntax in your
hledger journals (with an amountless extra posting to help transactions
balance, when needed), and use the print command to export to Ledger
or Beancount when you want to calculate lots and capital gains.
Ledger fixed lot costs
{=UNITCOST}and{{=TOTALCOST}}(fixed price)- when buying, means "this cost is also the fixed value, don't let it fluctuate in value reports"
Probably equivalent to @/@@, I'm not sure.
Beancount has simpler notation and different behaviour:
@ UNITCOSTand@@ TOTALCOST- expresses a cost without creating a lot, as in hledger
- when buying (acquiring) or selling (disposing of) a lot, and
combined with
{...}: is not used except to document the cost/selling price
{UNITCOST}and{{TOTALCOST}}- when buying, expresses the cost for transaction balancing, and also creates a lot with this cost basis attached
- when selling,
- selects a lot by its cost basis
- raises an error if that lot is not present or can not be selected unambiguously (depending on booking method configured)
- expresses the selling price for transaction balancing
{},{YYYY-MM-DD},{"LABEL"},{UNITCOST, "LABEL"},{UNITCOST, YYYY-MM-DD, "LABEL"}- when selling, other combinations of date/cost/label, like the above, are accepted for selecting the lot.
Currently, hledger
- supports
@and@@ - accepts the
{UNITCOST}/{{TOTALCOST}}notation, but ignores it - and rejects the rest.
CSV
hledger can read transactions from CSV (comma-separated values) files.
More precisely, it can read
DSV
(delimiter-separated values), from a file or standard input.
Comma-separated, semicolon-separated and tab-separated are the most
common variants, and hledger will recognise these three automatically
based on a .csv, .ssv or .tsv file name extension or a csv:,
ssv: or tsv: file path prefix.
(To learn about producing CSV or TSV output, see Output format.)
Each CSV file must be described by a corresponding rules file. This contains rules describing the CSV data (header line, fields layout, date format etc.), how to construct hledger transactions from it, and how to categorise transactions based on description or other attributes.
By default, hledger expects this rules file to be named like the CSV
file, with an extra .rules extension added, in the same directory. Eg
when asked to read foo/FILE.csv, hledger looks for
foo/FILE.csv.rules. You can specify a different rules file with the
--rules option.
At minimum, the rules file must identify the date and amount fields, and often it also specifies the date format and how many header lines there are. Here's a simple CSV file and a rules file for it:
Date, Description, Id, Amount
12/11/2019, Foo, 123, 10.23
# basic.csv.rules
skip 1
fields date, description, , amount
date-format %d/%m/%Y
$ hledger print -f basic.csv
2019-11-12 Foo
expenses:unknown 10.23
income:unknown -10.23
There's an introductory Tutorial: Import CSV data on hledger.org, and more CSV rules examples below, and a larger collection at https://github.com/simonmichael/hledger/tree/master/examples/csv.
CSV rules cheatsheet
The following kinds of rule can appear in the rules file, in any order.
(Blank lines and lines beginning with # or ; or * are ignored.)
source | optionally declare which file to read data from |
archive | optionally enable an archive of imported files |
encoding | optionally declare which text encoding the data has |
separator | declare the field separator, instead of relying on file extension |
decimal-mark | declare the decimal mark used in CSV amounts, when ambiguous |
date-format | declare how to parse CSV dates/date-times |
timezone | declare the time zone of ambiguous CSV date-times |
newest-first | improve txn order when: there are multiple records, newest first, all with the same date |
intra-day-reversed | improve txn order when: same-day txns are in opposite order to the overall file |
skip | (at top level) skip header line(s) at start of file |
fields list | name CSV fields for easy reference, and optionally assign their values to hledger fields |
| Field assignment | assign a CSV value or interpolated text value to a hledger field |
if block | conditionally assign values to hledger fields, or skip a record or end (skip rest of file) |
if table | conditionally assign values to hledger fields, using compact syntax |
skip | (inside an if rule) skip current record(s) |
end | (inside an if rule) skip all remaining records |
balance-type | select which type of balance assertions/assignments to generate |
include | inline another CSV rules file |
Working with CSV tips can be found below, including How CSV rules are evaluated.
source
If you tell hledger to read a csv file with -f foo.csv, it will look
for rules in foo.csv.rules. Or, you can tell it to read the rules
file, with -f foo.csv.rules, and it will look for data in foo.csv
(since 1.30). These are mostly equivalent, but the second method
provides some extra features. For one, the data file can be missing,
without causing an error; it is just considered empty.
For more flexibility, add a source rule, which lets you specify a
different data file:
source ./Checking1.csv
If the file does not exist, it is just considered empty, without raising an error.
If you specify just a file name with no path, hledger will look for it
in the ~/Downloads folder:
source Checking1.csv
You can use a glob pattern, to avoid specifying the file name exactly:
source Checking1*.csv
This has another benefit: if the pattern matches multiple files, hledger will read the newest (most recently modified) one. This avoids problems if you have downloaded a file multiple times without cleaning up.
All this enables a convenient workflow where can you just download CSV
files, then run hledger import rules/*.
See also "Working with CSV > Reading files specified by rule".
Data cleaning / data generating commands
After source's file pattern, you can write | (pipe) and a data
cleaning command (or command pipeline). If hledger's CSV rules aren't
enough, you can pre-process the downloaded data here with a shell
command or script, to make it more suitable for conversion. The command
will be executed by your default shell, in the directory of the rules
file, will receive the data file's content as standard input, and
should output zero or more lines of character-separated-values, suitable
for conversion by the CSV rules.
Examples:
source ./paypal.json | paypalcsv
source data/simplefin.json | simplefincsv - 'chase.*card'
source OfxDownload*.csv | grep -vE '^(([^,]*,){6}[^,]*|)$' | sort -t, -n +2
source History_for_Account_Z20144832*.csv # | grep -E '^([^,]*,){12}[^,]*$' | sed -E -e 's/^ //' -e 's/\.([0-9]),/.\10,/g' -e 's/,([0-9]+),/,\1.00,/g'
Or, after source you can write | and a data generating command (with
no file pattern before the |). This command receives no input, and
should output zero or more lines of character-separated values, suitable
for conversion by the CSV rules.
Examples:
source | paypaljson | paypalcsv
source | paypalcsv data/paypal.json
source | simplefinjson >data/simplefin.json && simplefincsv data/simplefin.json 'chase.*card'
source | simplefincsv data/simplefin.json 'unify.*checking'
(paypal* and simplefin* scripts are in
bin/)
Whenever hledger runs one of these commands, it will echo the command on stderr. If the command produces error output, but exits successfully, hledger will show the error output as a warning. If the command fails, hledger will fail and show the error output in the error message.
Added in 1.50; experimental.
archive
With archive added to a rules file, the import command will archive
each successfully processed data file or data command output in a nearby
data/ directory. The archive file name will be based on the rules file
and the data file's modification date and extension (or for a
data-generating command, the current date and the ".csv" extension).
The original data file, if any, will be removed.
Also, in this mode import will prefer the oldest file matched by the
source rule's glob pattern, not the newest. (So if there are multiple
downloads, they will be imported and archived oldest first.)
Archiving is optional, but it can be useful for troubleshooting your CSV rules, regenerating entries with improved rules, checking for variations in your bank's CSV, etc.
Added in 1.50; experimental.
encoding
encoding ENCODING
hledger normally expects non-ascii text to be using the system locale's
text encoding. If you need to read CSV files which have some other
encoding, you can do it by adding encoding ENCODING to your CSV rules.
Eg: encoding iso-8859-1.
The following encodings are supported:
ascii, utf-8, utf-16, utf-32, iso-8859-1, iso-8859-2,
iso-8859-3, iso-8859-4, iso-8859-5, iso-8859-6, iso-8859-7,
iso-8859-8, iso-8859-9, iso-8859-10, iso-8859-11, iso-8859-13,
iso-8859-14, iso-8859-15, iso-8859-16, cp1250, cp1251,
cp1252, cp1253, cp1254, cp1255, cp1256, cp1257, cp1258,
koi8-r, koi8-u, gb18030, macintosh, jis-x-0201, jis-x-0208,
iso-2022-jp, shift-jis, cp437, cp737, cp775, cp850, cp852,
cp855, cp857, cp860, cp861, cp862, cp863, cp864, cp865,
cp866, cp869, cp874, cp932.
Added in 1.42.
separator
You can use the separator rule to read other kinds of
character-separated data. The argument is any single separator
character, or the words tab or space (case insensitive). Eg, for
comma-separated values (CSV):
separator ,
or for semicolon-separated values (SSV):
separator ;
or for tab-separated values (TSV):
separator TAB
If the input file has a .csv, .ssv or .tsv file
extension (or a csv:, ssv:, tsv: prefix), the
appropriate separator will be inferred automatically, and you won't
need this rule.
skip
skip N
The word skip followed by a number (or no number, meaning 1) tells
hledger to ignore this many non-empty lines at the start of the input
data. You'll need this whenever your CSV data contains header lines.
Note, empty and blank lines are skipped automatically, so you don't
need to count those.
skip has a second meaning: it can be used inside if
blocks (described below), to skip one or more records
whenever the condition is true. Records skipped in this way are ignored,
except they are still required to be valid CSV.
date-format
date-format DATEFMT
This is a helper for the date (and date2) fields. If your CSV dates
are not formatted like YYYY-MM-DD, YYYY/MM/DD or YYYY.MM.DD,
you'll need to add a date-format rule describing them with a
strptime-style date parsing pattern - see
https://hackage.haskell.org/package/time/docs/Data-Time-Format.html#v:formatTime.
The pattern must parse the CSV date value completely. Some examples:
# MM/DD/YY
date-format %m/%d/%y
# D/M/YYYY
# The - makes leading zeros optional.
date-format %-d/%-m/%Y
# YYYY-Mmm-DD
date-format %Y-%h-%d
# M/D/YYYY HH:MM AM some other junk
# Note the time and junk must be fully parsed, though only the date is used.
date-format %-m/%-d/%Y %l:%M %p some other junk
Note currently there is no locale awareness for things like %b, and
setting LC_TIME won't help.
timezone
timezone TIMEZONE
When CSV contains date-times that are implicitly in some time zone other than yours, but containing no explicit time zone information, you can use this rule to declare the CSV's native time zone, which helps prevent off-by-one dates.
When the CSV date-times do contain time zone information, you don't
need this rule; instead, use %Z in date-format (or %z, %EZ,
%Ez; see the formatTime link above).
In either of these cases, hledger will do a time-zone-aware conversion, localising the CSV date-times to your current system time zone. If you prefer to localise to some other time zone, eg for reproducibility, you can (on unix at least) set the output timezone with the TZ environment variable, eg:
$ TZ=-1000 hledger print -f foo.csv # or TZ=-1000 hledger import foo.csv
timezone currently does not understand timezone names, except "UTC",
"GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST",
or "PDT". For others, use numeric format: +HHMM or -HHMM.
newest-first
hledger tries to ensure that the generated transactions will be ordered chronologically, including same-day transactions. Usually it can auto-detect how the CSV records are ordered. But if it encounters CSV where all records are on the same date, it assumes that the records are oldest first. If in fact the CSV's records are normally newest first, like:
2022-10-01, txn 3...
2022-10-01, txn 2...
2022-10-01, txn 1...
you can add the newest-first rule to help hledger generate the
transactions in correct order.
# same-day CSV records are newest first
newest-first
intra-day-reversed
If CSV records within a single day are ordered opposite to the overall
record order, you can add the intra-day-reversed rule to improve the
order of journal entries. Eg, here the overall record order is newest
first, but same-day records are oldest first:
2022-10-02, txn 3...
2022-10-02, txn 4...
2022-10-01, txn 1...
2022-10-01, txn 2...
# transactions within each day are reversed with respect to the overall date order
intra-day-reversed
decimal-mark
decimal-mark .
or:
decimal-mark ,
hledger automatically accepts either period or comma as a decimal mark when parsing numbers (cf Amounts). However if any numbers in the CSV contain digit group marks, such as thousand-separating commas, you should declare the decimal mark explicitly with this rule, to avoid misparsed numbers.
CSV fields and hledger fields
This can be confusing, so let's start with an overview:
-
CSV fields are provided by your data file. They are named by their position in the CSV record, starting with 1. You can also give them a readable name.
-
hledger fields are predefined;
date,description,account1,amount1,account2are some of them. They correspond to parts of a transaction's journal entry, mostly. -
The CSV fields and hledger fields are the only fields you'll be working with; you can't define new fields, or variables as in a programming language. (But you could add extra CSV fields to the data in preprocessing, before running the rules.)
-
For each CSV record, you'll assign values to one or more of the hledger fields to build up a transaction (journal entry). Values can be static text, CSV field values from the current record, or a combination of these.
-
For simple cases, you can give a CSV field the same name as one of the hledger fields, then its value will be automatically assigned to that hledger field.
-
CSV fields can only be read, not written to. They'll be on the right hand side, with a % prefix. Eg
- testing a CSV field's value:
if %CSVFIELD ... - interpolating its value:
HLEDGERFIELD %CSVFIELD
- testing a CSV field's value:
-
hledger fields can only be written to, not read. They'll be on the left hand side (or in a fields list), with no prefix. Eg
- setting the transaction's description to a value:
description VALUE - setting the transaction's description to the second CSV field's
value:
fields date, description, amount
- setting the transaction's description to a value:
fields list
fields FIELDNAME1, FIELDNAME2, ...
A fields list (the word fields followed by comma-separated field
names) is optional, but convenient. It does two things:
-
It names the CSV field in each column. This can be convenient if you are referencing them in other rules, so you can say
%SomeFieldinstead of remembering%13. -
Whenever you use one of the special hledger field names (described below), it assigns the CSV value in this position to that hledger field. This is the quickest way to populate hledger's fields and build a transaction.
Here's an example that says "use the 1st, 2nd and 4th fields as the transaction's date, description and amount; name the last two fields for later reference; and ignore the others":
fields date, description, , amount, , , somefield, anotherfield
In a fields list, the separator is always comma; it is unrelated to the CSV file's separator. Also:
- There must be least two items in the list (at least one comma).
- Field names may not contain spaces. Spaces before/after field names are optional.
- Field names may contain
_(underscore) or-(hyphen). - Fields you don't care about can be given a dummy name or an empty name.
If the CSV contains column headings, it's convenient to use these for your field names, suitably modified (eg lower-cased with spaces replaced by underscores).
Sometimes you may want to alter a CSV field name to avoid assigning to a
hledger field with the same name. Eg you could call the CSV's
"balance" field balance_ to avoid directly setting hledger's
balance field (and generating a balance assertion).
Field assignment
HLEDGERFIELD FIELDVALUE
Field assignments are the more flexible way to assign CSV values to hledger fields. They can be used instead of or in addition to a fields list (see above).
To assign a value to a hledger field, write the field
name (any of the standard hledger field/pseudo-field
names, defined below), a space, followed by a text value on the same
line. This text value may interpolate CSV fields, referenced either by
their 1-based position in the CSV record (%N) or by the name they were
given in the fields list (%CSVFIELD), and regular expression match
groups (\N).
Some examples:
# set the amount to the 4th CSV field, with " USD" appended
amount %4 USD
# combine three fields to make a comment, containing note: and date: tags
comment note: %somefield - %anotherfield, date: %1
Tips:
- Interpolation strips outer whitespace (so a CSV value like
" 1 "becomes1when interpolated) (#1051). - Interpolations always refer to a CSV field - you can't interpolate a hledger field. (See Referencing other fields below).
Field names
Note the two kinds of field names mentioned here, and used only in hledger CSV rules files:
-
CSV field names (
CSVFIELDin these docs): you can optionally name the CSV columns for easy reference (since hledger doesn't yet automatically recognise column headings in a CSV file), by writing arbitrary names in afieldslist, eg:fields When, What, Some_Id, Net, Total, Foo, Bar -
Special hledger field names (
HLEDGERFIELDin these docs): you must set at least some of these to generate the hledger transaction from a CSV record, by writing them as the left hand side of a field assignment, eg:date %When code %Some_Id description %What comment %Foo %Bar amount1 $ %Totalor directly in a
fieldslist:fields date, description, code, , amount1, Foo, Bar currency $ comment %Foo %Bar
Here are all the special hledger field names available, and what happens when you assign values to them:
date field
Assigning to date sets the transaction date.
date2 field
date2 sets the transaction's secondary date, if
any.
status field
status sets the transaction's status, if any.
code field
code sets the transaction's code, if any.
description field
description sets the transaction's description, if
any.
comment field
comment sets the transaction's comment, if
any.
commentN, where N is a number, sets the Nth posting's comment.
You can assign multi-line comments by writing literal \n in the code.
A comment starting with \n will begin on a new line.
Comments can contain tags, as usual.
Posting comments can also contain a posting date. A secondary date, or a year-less date, will be ignored.
account field
Assigning to accountN, where N is 1 to 99, sets the account name of
the Nth posting, and causes that posting to be generated.
Most often there are two postings, so you'll want to set account1 and
account2. Typically account1 is associated with the CSV file, and is
set once with a top-level assignment, while account2 is set based on
each transaction's description, in conditional rules.
If a posting's account name is left unset but its amount is set (see below), a default account name will be chosen (like "expenses:unknown" or "income:unknown").
amount field
There are several ways to set posting amounts from CSV, useful in different situations.
-
amountis the oldest and simplest. Assigning to this sets the amount of the first and second postings. In the second posting, the amount will be negated; also, if it has a cost attached, it will be converted to cost. -
amount-inandamount-outwork exactly like the above, but should be used when the CSV has two amount fields (such as "Debit" and "Credit", or "Inflow" and "Outflow"). Whichever field has a non-zero value will be used as the amount of the first and second postings. Here are some tips to avoid confusion:- It's not "amount-in for posting 1 and amount-out for posting 2", it is "extract a single amount from the amount-in or amount-out field, and use that for posting 1 and (negated) for posting 2".
- Don't use both
amountandamount-in/amount-outin the same rules file; choose based on whether the amount is in a single CSV field or spread across two fields. - In each record, at most one of the two CSV fields should contain a non-zero amount; the other field must contain a zero or nothing.
- hledger assumes both CSV fields contain unsigned numbers, and it automatically negates the amount-out values.
- If the data doesn't fit these requirements, you'll probably need an if rule (see below).
-
amountN(where N is a number from 1 to 99) sets the amount of only a single posting: the Nth posting in the transaction. You'll usually need at least two such assignments to make a balanced transaction. You can also generate more than two postings, to represent more complex transactions. The posting numbers don't have to be consecutive; with if rules, higher posting numbers can be useful to ensure a certain order of postings. -
amountN-inandamountN-outwork exactly like the above, but should be used when the CSV has two amount fields. This is analogous toamount-inandamount-out, and those tips also apply here. -
Remember that a
fieldslist can also do assignments. So in a fields list if you name a CSV field "amount", that counts as assigning toamount. (If you don't want that, call it something else in the fields list, like "amount_".) -
The above don't handle every situation; if you need more flexibility, use an
ifrule to set amounts conditionally. See "Working with CSV > Setting amounts" below for more on this and on amount-setting generally.
currency field
currency sets a currency symbol, to be prepended to all postings'
amounts. You can use this if the CSV amounts do not have a currency
symbol, eg if it is in a separate column.
currencyN prepends a currency symbol to just the Nth posting's
amount.
balance field
balanceN sets a balance assertion amount (or if
the posting amount is left empty, a balance
assignment) on posting N.
balance is a compatibility spelling for hledger <1.17; it is
equivalent to balance1.
You can adjust the type of assertion/assignment with the balance-type
rule (see below).
See the Working with CSV tips below for more about setting amounts and currency.
if block
Rules can be applied conditionally, depending on patterns in the CSV data. This allows flexibility; in particular, it is how you can categorise transactions, selecting an appropriate account name based on their description (for example). There are two ways to write conditional rules: "if blocks", described here, and "if tables", described below.
An if block is the word if and one or more "matcher" expressions
(can be a word or phrase), one per line, starting either on the same or
next line; followed by one or more indented rules. Eg,
if MATCHER
RULE
or
if
MATCHER
MATCHER
MATCHER
RULE
RULE
If any of the matchers succeeds, all of the indented rules will be applied. They are usually field assignments, but the following special rules may also be used within an if block:
skip- skips the matched CSV record (generating no transaction from it)end- skips the rest of the current CSV file.
Some examples:
# if the record contains "groceries", set account2 to "expenses:groceries"
if groceries
account2 expenses:groceries
# if the record contains any of these phrases, set account2 and a transaction comment as shown
if
monthly service fee
atm transaction fee
banking thru software
account2 expenses:business:banking
comment XXX deductible ? check it
# if an empty record is seen (assuming five fields), ignore the rest of the CSV file
if ,,,,
end
Matchers
There are two kinds of matcher:
-
A whole record matcher is simplest: it is just a word, single-line text fragment, or other regular expression, which hledger will try to match case-insensitively anywhere within the CSV record.
Eg:whole foods. -
A field matcher has a percent-prefixed CSV field number or name before the pattern.
Eg:%3 whole foodsor%description whole foods.
hledger will try to match the pattern just within the named CSV field.
When using these, there's two things to be aware of:
-
Whole record matchers don't see the exact original record; they see a reconstruction of it, in which values are comma-separated, and quotes enclosing values and whitespace outside those quotes are removed.
Eg when reading an SSV record like:2023-01-01 ; "Acme, Inc. " ; 1,000
the whole record matcher sees instead:2023-01-01,Acme, Inc. ,1,000 -
Field matchers expect either a CSV field number, or a CSV field name declared with
fields. Anything else will cause it to match against the empty string, and probably fail silently (this makes it easier to reuse common rules with different CSV files). Don't use a hledger field name here (see CSV fields and hledger fields).
You can also prefix a matcher with ! (and optional space) to negate
it. Eg ! whole foods, ! %3 whole foods, !%description whole foods
will match if "whole foods" is NOT present. Added in 1.32.
The pattern is, as usual in hledger, a POSIX extended regular expression
that also supports GNU word boundaries (\b, \B, \<, \>) and
nothing else. For more details and tips, see Regular expressions in CSV
rules below.
Multiple matchers
When an if block has multiple matchers, each on its own line,
- By default they are OR'd (any of them can match).
- Matcher lines beginning with
&(or&&, since 1.42) are AND'ed with the matcher above (all in the AND'ed group must match). - Matcher lines beginning with
& !(since 1.41, or&& !, since 1.42) are first negated and then AND'ed with the matcher above.
You can also combine multiple matchers one the same line separated by
&& (AND) or && ! (AND NOT). Eg
%description amazon && %date 2025-01-01 will match only when the
description field contains "amazon" and the date field contains
"2025-01-01". Added in 1.42.
Match groups
Added in 1.32
Matchers can define match groups: parenthesised portions of the regular
expression which are available for reference in field assignments.
Groups are enclosed in regular parentheses (( and )) and can be
nested. Each group is available in field assignments using the token
\N, where N is an index into the match groups for this conditional
block (e.g. \1, \2, etc.).
Example: Warp credit card payment postings to the beginning of the billing period (Month start), to match how they are presented in statements, using posting dates:
if %date (....-..)-..
comment2 date:\1-01
Another example: Read the expense account from the CSV field, but throw away a prefix:
if %account1 liabilities:family:(expenses:.*)
account1 \1
if table
"if tables" are an alternative to if blocks; they can express many matchers and field assignments in a more compact tabular format, like this:
if,HLEDGERFIELD1,HLEDGERFIELD2,...
MATCHERA,VALUE1,VALUE2,...
MATCHERB && MATCHERC,VALUE1,VALUE2,... (*since 1.42*)
; Comment line that explains MATCHERD
MATCHERD,VALUE1,VALUE2,...
<empty line>
The first character after if is taken to be this if table's field
separator. It is unrelated to the separator used in the CSV file. It
should be a non-alphanumeric character like , or | that does not
appear anywhere else in the table (it should not be used in field names
or matchers or values, and it cannot be escaped with a backslash).
Each line must contain the same number of separators; empty values are allowed. Whitespace can be used in the matcher lines for readability (but not in the if line, currently). You can use the comment lines in the table body. The table must be terminated by an empty line (or end of file).
An if table like the above is interpreted as follows: try all of the
lines with matchers; whenever a line with matchers succeeds, assign all
of the values on that line to the corresponding hledger fields; If
multiple lines match, later lines will override fields assigned by the
earlier ones - just like the sequence of if blocks would behave.
If table presented above is equivalent to this sequence of if blocks:
if MATCHERA
HLEDGERFIELD1 VALUE1
HLEDGERFIELD2 VALUE2
...
if MATCHERB && MATCHERC
HLEDGERFIELD1 VALUE1
HLEDGERFIELD2 VALUE2
...
; Comment line which explains MATCHERD
if MATCHERD
HLEDGERFIELD1 VALUE1
HLEDGERFIELD2 VALUE2
...
Example:
if,account2,comment
atm transaction fee,expenses:business:banking,deductible? check it
%description groceries,expenses:groceries,
;; Comment line that desribes why this particular date is special
2023/01/12.*Plumbing LLC,expenses:house:upkeep,emergency plumbing call-out
balance-type
Balance assertions generated by assigning to
balanceN are of the simple = type by default,
which is a single-commodity,
subaccount-excluding assertion. You may
find the subaccount-including variants more useful, eg if you have
created some virtual subaccounts of checking to help with budgeting. You
can select a different type of assertion with the balance-type rule:
# balance assertions will consider all commodities and all subaccounts
balance-type ==*
Here are the balance assertion types for quick reference:
= single commodity, exclude subaccounts
=* single commodity, include subaccounts
== multi commodity, exclude subaccounts
==* multi commodity, include subaccounts
include
include RULESFILE
This includes the contents of another CSV rules file at this point.
RULESFILE is an absolute file path or a path relative to the current
file's directory. This can be useful for sharing common rules between
several rules files, eg:
# someaccount.csv.rules
## someaccount-specific rules
fields date,description,amount
account1 assets:someaccount
account2 expenses:misc
## common rules
include categorisation.rules
Working with CSV
Some tips:
Rapid feedback
It's a good idea to get rapid feedback while creating/troubleshooting CSV rules. Here's a good way, using entr from eradman.com/entrproject:
$ ls foo.csv* | entr bash -c 'echo ----; hledger -f foo.csv print desc:SOMEDESC'
A desc: query (eg) is used to select just one, or a few, transactions of interest. "bash -c" is used to run multiple commands, so we can echo a separator each time the command re-runs, making it easier to read the output.
Valid CSV
Note that hledger will only accept valid CSV conforming to RFC 4180, and equivalent SSV and TSV formats (like RFC 4180 but with semicolon or tab as separators). This means, eg:
- Values may be enclosed in double quotes, or not. Enclosing in single
quotes is not allowed. (Eg
'A','B'is rejected.) - When values are enclosed in double quotes, spaces outside the quotes
are not
allowed.
(Eg
"A", "B"is rejected.) - When values are not enclosed in quotes, they may not contain double
quotes. (Eg
A"A, Bis rejected.)
If your CSV/SSV/TSV is not valid in this sense, you'll need to transform it before reading with hledger. Try using sed, or a more permissive CSV parser like python's csv lib.
File Extension
To help hledger choose the CSV file reader and show the right error
messages (and choose the right field separator character by default),
it's best if CSV/SSV/TSV files are named with a .csv, .ssv or
.tsv filename extension. (More about this at Data
formats.)
When reading files with the "wrong" extension, you can ensure the CSV
reader (and the default field separator) by prefixing the file path with
csv:, ssv: or tsv:: Eg:
$ hledger -f ssv:foo.dat print
You can also override the default field separator with a separator rule if needed.
Reading CSV from standard input
You'll need the file format prefix when reading CSV from stdin also, since hledger assumes journal format by default. Eg:
$ cat foo.dat | hledger -f ssv:- print
Reading multiple CSV files
If you use multiple -f options to read multiple CSV files at once,
hledger will look for a correspondingly-named rules file for each CSV
file. But if you specify a rules file with --rules, that rules file
will be used for all the CSV files.
Reading files specified by rule
Instead of specifying a CSV file in the command line, you can specify a
rules file, as in hledger -f foo.csv.rules CMD. By default this will
read data from foo.csv in the same directory, but you can add a
source rule to specify a different data file, perhaps located
in your web browser's download directory.
This feature was added in hledger 1.30, so you won't see it in most CSV
rules examples. But it helps remove some of the busywork of managing CSV
downloads. Most of your financial institutions's default CSV filenames
are different and can be recognised by a glob pattern. So you can put a
rule like source Checking1*.csv in foo-checking.csv.rules, and then
periodically follow a workflow like:
- Download CSV from Foo's website, using your browser's defaults
- Run
hledger import foo-checking.csv.rulesto import any new transactions
After import, you can: discard the CSV, or leave it where it is for a
while, or move it into your archives, as you prefer. If you do nothing,
next time your browser will save something like Checking1-2.csv, and
hledger will use that because of the * wild card and because it is the
most recent.
Valid transactions
After reading a CSV file, hledger post-processes and validates the generated journal entries as it would for a journal file - balancing them, applying balance assignments, and canonicalising amount styles. Any errors at this stage will be reported in the usual way, displaying the problem entry.
There is one exception: balance assertions, if you have generated them, will not be checked, since normally these will work only when the CSV data is part of the main journal. If you do need to check balance assertions generated from CSV right away, pipe into another hledger:
$ hledger -f file.csv print | hledger -f- print
Deduplicating, importing
When you download a CSV file periodically, eg to get your latest bank transactions, the new file may overlap with the old one, containing some of the same records.
The import command will (a) detect the new transactions, and
(b) append just those transactions to your main journal. It is
idempotent, so you don't have to remember how many times you ran it or
with which version of the CSV. (It keeps state in a hidden
.latest.FILE.csv file.) This is the easiest way to import CSV data.
Eg:
# download the latest CSV files, then run this command.
# Note, no -f flags needed here.
$ hledger import *.csv [--dry]
This method works for most CSV files. (Where records have a stable chronological order, and new records appear only at the new end.)
A number of other tools and workflows, hledger-specific and otherwise, exist for converting, deduplicating, classifying and managing CSV data. See:
- https://hledger.org/cookbook.html#setups-and-workflows
- https://plaintextaccounting.org -> data import/conversion
Regular expressions in CSV rules
Regular expressions in if conditions (AKA matchers) are POSIX extended
regular expressions, that also support GNU word boundaries (\b, \B,
\<, \>), and nothing else. (For more detail, see Regular
expressions.)
Here are some examples that might be useful in CSV rules:
- Is field "foo" truly empty ?
if %foo ^$ - Is it empty or containing only whitespace ?
if %foo ^ *$ - Is it non-empty ?
if %foo . - Does it contain non-whitespace ?
if %foo [^ ]
Testing the value of numeric fields is a little harder. You can't use
hledger queries like amt:0 or amt:>10 in CSV rules. But you can
often achieve the same thing with a regular expression.
Note the content and layout of number fields in CSV varies, and can change over time (eg if you switch data providers). So numeric regexps are always somewhat specific to your particular CSV data; and it's a good idea to make them defensive and robust if you can.
Here are some examples:
- Does foo contain a non-zero number ?
if %foo [1-9] - Is it negative ?
if %foo - - Is it non-negative ?
if ! %foo - - Is it >= 10 ?
if %foo [1-9][0-9]+\.(assuming a decimal period and no leading zeros) - Is it >= 10 and < 20 ?
if %foo \b1[0-9]\.
Setting amounts
Continuing from amount field above, here are more tips for amount-setting:
-
If the amount is in a single CSV field:\
a. If its sign indicates direction of flow:
Assign it toamountN, to set the Nth posting's amount. N is usually 1 or 2 but can go up to 99.b. If another field indicates direction of flow:
Use one or more conditional rules to set the appropriate amount sign. Eg:# assume a withdrawal unless Type contains "deposit": amount1 -%Amount if %Type deposit amount1 %Amount -
If the amount is in two CSV fields (such as Debit and Credit, or In and Out):\
a. If both fields are unsigned:
Assign one field toamountN-inand the other toamountN-out. hledger will automatically negate the "out" field, and will use whichever field value is non-zero as posting N's amount.b. If either field is signed:
You will probably need to override hledger's sign for one or the other field, as in the following example:# Negate the -out value, but only if it is not empty: fields date, description, amount1-in, amount1-out if %amount1-out [1-9] amount1-out -%amount1-outc. If both fields can contain a non-zero value (or both can be empty):
The -in/-out rules normally choose the value which is non-zero/non-empty. Some value pairs can be ambiguous, such as1andnone. For such cases, use conditional rules to help select the amount. Eg, to handle the above you could select the value containing non-zero digits:fields date, description, in, out if %in [1-9] amount1 %in if %out [1-9] amount1 %out -
If you want posting 2's amount converted to cost:
Use the unnumberedamount(oramount-inandamount-out) syntax. -
If the CSV has only balance amounts, not transaction amounts:
Assign tobalanceN, to set a balance assignment on the Nth posting, causing the posting's amount to be calculated automatically.balancewith no number is equivalent tobalance1. In this situation hledger is more likely to guess the wrong default account name, so you may need to set that explicitly.
Amount signs
There is some special handling making it easier to parse and to reverse
amount signs. (This only works for whole amounts, not for cost amounts
such as COST in amount1 AMT @ COST):
-
If an amount value begins with a plus sign:
that will be removed:+AMTbecomesAMT -
If an amount value is parenthesised:
it will be de-parenthesised and sign-flipped:(AMT)becomes-AMT -
If an amount value has two minus signs (or two sets of parentheses, or a minus sign and parentheses):
they cancel out and will be removed:--AMTor-(AMT)becomesAMT -
If an amount value contains just a sign (or just a set of parentheses):
that is removed, making it an empty value."+"or"-"or"()"becomes"".
It's not possible (without preprocessing the CSV) to set an amount to its absolute value, ie discard its sign.
Setting currency/commodity
If the currency/commodity symbol is included in the CSV's amount field(s):
2023-01-01,foo,$123.00
you don't have to do anything special for the commodity symbol, it will be assigned as part of the amount. Eg:
fields date,description,amount
2023-01-01 foo
expenses:unknown $123.00
income:unknown $-123.00
If the currency is provided as a separate CSV field:
2023-01-01,foo,USD,123.00
You can assign that to the currency pseudo-field, which has the
special effect of prepending itself to every amount in the transaction
(on the left, with no separating space):
fields date,description,currency,amount
2023-01-01 foo
expenses:unknown USD123.00
income:unknown USD-123.00
Or, you can use a field assignment to construct the amount yourself, with more control. Eg to put the symbol on the right, and separated by a space:
fields date,description,cur,amt
amount %amt %cur
2023-01-01 foo
expenses:unknown 123.00 USD
income:unknown -123.00 USD
Note we used a temporary field name (cur) that is not currency -
that would trigger the prepending effect, which we don't want here.
Amount decimal places
When you are reading CSV data, eg with a command like
hledger -f foo.csv print, hledger will infer each commodity's decimal
precision (and other commodity display
styles) from the amounts - much as when
reading a journal file without commodity directives (see the link).
Note, the commodity styles are not inferred from the numbers in the original CSV data; rather, they are inferred from the amounts generated by the CSV rules.
When you are importing CSV data with the import command, eg
hledger import foo.csv, there's another step: import tries to make
the new entries conform to the journal's
existing styles. So for each commodity - let's say it's EUR - import
will choose:
- the style declared for EUR by a
commoditydirective in the journal - otherwise, the style inferred from EUR amounts in the journal
- otherwise, the style inferred from EUR amounts generated by the CSV rules.
TLDR: if import is not generating the precisions or styles you want,
add a commodity directive to specify them.
Referencing other fields
In field assignments, you can interpolate only CSV fields, not hledger fields. In the example below, there's both a CSV field and a hledger field named amount1, but %amount1 always means the CSV field, not the hledger field:
# Name the third CSV field "amount1"
fields date,description,amount1
# Set hledger's amount1 to the CSV amount1 field followed by USD
amount1 %amount1 USD
# Set comment to the CSV amount1 (not the amount1 assigned above)
comment %amount1
Here, since there's no CSV amount1 field, %amount1 will produce a literal "amount1":
fields date,description,csvamount
amount1 %csvamount USD
# Can't interpolate amount1 here
comment %amount1
When there are multiple field assignments to the same hledger field, only the last one takes effect. Here, comment's value will be be B, or C if "something" is matched, but never A:
comment A
comment B
if something
comment C
How CSV rules are evaluated
Here's how to think of CSV rules being evaluated. If you get a confusing error while reading a CSV file, it may help to try to understand which of these steps is failing:
-
Any included rules files are inlined, from top to bottom, depth first (scanning each included file for further includes, recursively, before proceeding).
-
Top level rules (
date-format,fields,newest-first,skipetc) are read, top to bottom. "Top level rules" means non-conditional rules. If a rule occurs more than once, the last one wins; except forskip/endrules, where the first one wins. -
The CSV file is read as text. Any non-ascii characters will be decoded using the text encoding specified by the
encodingrule, otherwise the system locale's text encoding. -
Any top-level skip or end rule is applied.
skip [N]immediately skips the current or next N CSV records;endimmediately skips all remaining CSV records (not normally used at top level). -
Now any remaining CSV records are processed. For each CSV record, in file order:
-
Is there a conditional skip/end rule that applies for this record ? Search the
ifblocks, from top to bottom, for a succeeding one containing askiporendrule. If found, skip the specified number of CSV records, then continue at 5.
Otherwise... -
Do some basic validation on this CSV record (eg, check that it has at least two fields).
-
For each hledger field (
date,description,account1, etc.):-
Get the field's assigned value, first searching top level assignments, made directly or by the
fieldsrule, then assignments made inside succeedingifblocks. If there are more than one, the last one wins. -
Compute the field's actual value (as text), by interpolating any %CSVFIELD references within the assigned value; or by choosing a default value if there was no assignment.
-
-
Generate a hledger transaction from the hledger field values, parsing them if needed (eg from text to an amount).
-
This is all done by the CSV reader, one of several readers hledger can use to read transactions from an input file. When all input files have been read successfully, their transactions are passed to whichever hledger command the user specified.
Well factored rules
Some things than can help reduce duplication and complexity in rules files:
-
Extracting common rules usable with multiple CSV files into a
common.rules, and addinginclude common.rulesto each CSV's rules file. -
Splitting if blocks into smaller if blocks, extracting the frequently used parts.
CSV rules examples
Bank of Ireland
Here's a CSV with two amount fields (Debit and Credit), and a balance field, which we can use to add balance assertions, which is not necessary but provides extra error checking:
Date,Details,Debit,Credit,Balance
07/12/2012,LODGMENT 529898,,10.0,131.21
07/12/2012,PAYMENT,5,,126
# bankofireland-checking.csv.rules
# skip the header line
skip
# name the csv fields, and assign some of them as journal entry fields
fields date, description, amount-out, amount-in, balance
# We generate balance assertions by assigning to "balance"
# above, but you may sometimes need to remove these because:
#
# - the CSV balance differs from the true balance,
# by up to 0.0000000000005 in my experience
#
# - it is sometimes calculated based on non-chronological ordering,
# eg when multiple transactions clear on the same day
# date is in UK/Ireland format
date-format %d/%m/%Y
# set the currency
currency EUR
# set the base account for all txns
account1 assets:bank:boi:checking
$ hledger -f bankofireland-checking.csv print
2012-12-07 LODGMENT 529898
assets:bank:boi:checking EUR10.0 = EUR131.2
income:unknown EUR-10.0
2012-12-07 PAYMENT
assets:bank:boi:checking EUR-5.0 = EUR126.0
expenses:unknown EUR5.0
The balance assertions don't raise an error above, because we're reading directly from CSV, but they will be checked if these entries are imported into a journal file.
Coinbase
A simple example with some CSV from Coinbase. The spot price is recorded
using cost notation. The legacy amount field name conveniently sets
amount 2 (posting 2's amount) to the total cost.
# Timestamp,Transaction Type,Asset,Quantity Transacted,Spot Price Currency,Spot Price at Transaction,Subtotal,Total (inclusive of fees and/or spread),Fees and/or Spread,Notes
# 2021-12-30T06:57:59Z,Receive,USDC,100,GBP,0.740000,"","","","Received 100.00 USDC from an external account"
# coinbase.csv.rules
skip 1
fields Timestamp,Transaction_Type,Asset,Quantity_Transacted,Spot_Price_Currency,Spot_Price_at_Transaction,Subtotal,Total,Fees_Spread,Notes
date %Timestamp
date-format %Y-%m-%dT%T%Z
description %Notes
account1 assets:coinbase:cc
amount %Quantity_Transacted %Asset @ %Spot_Price_at_Transaction %Spot_Price_Currency
$ hledger print -f coinbase.csv
2021-12-30 Received 100.00 USDC from an external account
assets:coinbase:cc 100 USDC @ 0.740000 GBP
income:unknown -74.000000 GBP
Amazon
Here we convert amazon.com order history, and use an if block to generate a third posting if there's a fee. (In practice you'd probably get this data from your bank instead, but it's an example.)
"Date","Type","To/From","Name","Status","Amount","Fees","Transaction ID"
"Jul 29, 2012","Payment","To","Foo.","Completed","$20.00","$0.00","16000000000000DGLNJPI1P9B8DKPVHL"
"Jul 30, 2012","Payment","To","Adapteva, Inc.","Completed","$25.00","$1.00","17LA58JSKRD4HDGLNJPI1P9B8DKPVHL"
# amazon-orders.csv.rules
# skip one header line
skip 1
# name the csv fields, and assign the transaction's date, amount and code.
# Avoided the "status" and "amount" hledger field names to prevent confusion.
fields date, _, toorfrom, name, amzstatus, amzamount, fees, code
# how to parse the date
date-format %b %-d, %Y
# combine two fields to make the description
description %toorfrom %name
# save the status as a tag
comment status:%amzstatus
# set the base account for all transactions
account1 assets:amazon
# leave amount1 blank so it can balance the other(s).
# I'm assuming amzamount excludes the fees, don't remember
# set a generic account2
account2 expenses:misc
amount2 %amzamount
# and maybe refine it further:
#include categorisation.rules
# add a third posting for fees, but only if they are non-zero.
if %fees [1-9]
account3 expenses:fees
amount3 %fees
$ hledger -f amazon-orders.csv print
2012-07-29 (16000000000000DGLNJPI1P9B8DKPVHL) To Foo. ; status:Completed
assets:amazon
expenses:misc $20.00
2012-07-30 (17LA58JSKRD4HDGLNJPI1P9B8DKPVHL) To Adapteva, Inc. ; status:Completed
assets:amazon
expenses:misc $25.00
expenses:fees $1.00
Paypal
Here's a real-world rules file for (customised) Paypal CSV, with some Paypal-specific rules, and a second rules file included:
"Date","Time","TimeZone","Name","Type","Status","Currency","Gross","Fee","Net","From Email Address","To Email Address","Transaction ID","Item Title","Item ID","Reference Txn ID","Receipt ID","Balance","Note"
"10/01/2019","03:46:20","PDT","Calm Radio","Subscription Payment","Completed","USD","-6.99","0.00","-6.99","[email protected]","[email protected]","60P57143A8206782E","MONTHLY - $1 for the first 2 Months: Me - Order 99309. Item total: $1.00 USD first 2 months, then $6.99 / Month","","I-R8YLY094FJYR","","-6.99",""
"10/01/2019","03:46:20","PDT","","Bank Deposit to PP Account ","Pending","USD","6.99","0.00","6.99","","[email protected]","0TU1544T080463733","","","60P57143A8206782E","","0.00",""
"10/01/2019","08:57:01","PDT","Patreon","PreApproved Payment Bill User Payment","Completed","USD","-7.00","0.00","-7.00","[email protected]","[email protected]","2722394R5F586712G","Patreon* Membership","","B-0PG93074E7M86381M","","-7.00",""
"10/01/2019","08:57:01","PDT","","Bank Deposit to PP Account ","Pending","USD","7.00","0.00","7.00","","[email protected]","71854087RG994194F","Patreon* Membership","","2722394R5F586712G","","0.00",""
"10/19/2019","03:02:12","PDT","Wikimedia Foundation, Inc.","Subscription Payment","Completed","USD","-2.00","0.00","-2.00","[email protected]","[email protected]","K9U43044RY432050M","Monthly donation to the Wikimedia Foundation","","I-R5C3YUS3285L","","-2.00",""
"10/19/2019","03:02:12","PDT","","Bank Deposit to PP Account ","Pending","USD","2.00","0.00","2.00","","[email protected]","3XJ107139A851061F","","","K9U43044RY432050M","","0.00",""
"10/22/2019","05:07:06","PDT","Noble Benefactor","Subscription Payment","Completed","USD","10.00","-0.59","9.41","[email protected]","[email protected]","6L8L1662YP1334033","Joyful Systems","","I-KC9VBGY2GWDB","","9.41",""
# paypal-custom.csv.rules
# Tips:
# Export from Activity -> Statements -> Custom -> Activity download
# Suggested transaction type: "Balance affecting"
# Paypal's default fields in 2018 were:
# "Date","Time","TimeZone","Name","Type","Status","Currency","Gross","Fee","Net","From Email Address","To Email Address","Transaction ID","Shipping Address","Address Status","Item Title","Item ID","Shipping and Handling Amount","Insurance Amount","Sales Tax","Option 1 Name","Option 1 Value","Option 2 Name","Option 2 Value","Reference Txn ID","Invoice Number","Custom Number","Quantity","Receipt ID","Balance","Address Line 1","Address Line 2/District/Neighborhood","Town/City","State/Province/Region/County/Territory/Prefecture/Republic","Zip/Postal Code","Country","Contact Phone Number","Subject","Note","Country Code","Balance Impact"
# This rules file assumes the following more detailed fields, configured in "Customize report fields":
# "Date","Time","TimeZone","Name","Type","Status","Currency","Gross","Fee","Net","From Email Address","To Email Address","Transaction ID","Item Title","Item ID","Reference Txn ID","Receipt ID","Balance","Note"
fields date, time, timezone, description_, type, status_, currency, grossamount, feeamount, netamount, fromemail, toemail, code, itemtitle, itemid, referencetxnid, receiptid, balance, note
skip 1
date-format %-m/%-d/%Y
# ignore some paypal events
if
In Progress
Temporary Hold
Update to
skip
# add more fields to the description
description %description_ %itemtitle
# save some other fields as tags
comment itemid:%itemid, fromemail:%fromemail, toemail:%toemail, time:%time, type:%type, status:%status_
# convert to short currency symbols
if %currency USD
currency $
if %currency EUR
currency E
if %currency GBP
currency P
# generate postings
# the first posting will be the money leaving/entering my paypal account
# (negative means leaving my account, in all amount fields)
account1 assets:online:paypal
amount1 %netamount
# the second posting will be money sent to/received from other party
# (account2 is set below)
amount2 -%grossamount
# if there's a fee, add a third posting for the money taken by paypal.
if %feeamount [1-9]
account3 expenses:banking:paypal
amount3 -%feeamount
comment3 business:
# choose an account for the second posting
# override the default account names:
# if the amount is positive, it's income (a debit)
if %grossamount ^[^-]
account2 income:unknown
# if negative, it's an expense (a credit)
if %grossamount ^-
account2 expenses:unknown
# apply common rules for setting account2 & other tweaks
include common.rules
# apply some overrides specific to this csv
# Transfers from/to bank. These are usually marked Pending,
# which can be disregarded in this case.
if
Bank Account
Bank Deposit to PP Account
description %type for %referencetxnid %itemtitle
account2 assets:bank:wf:pchecking
account1 assets:online:paypal
# Currency conversions
if Currency Conversion
account2 equity:currency conversion
# common.rules
if
darcs
noble benefactor
account2 revenues:foss donations:darcshub
comment2 business:
if
Calm Radio
account2 expenses:online:apps
if
electronic frontier foundation
Patreon
wikimedia
Advent of Code
account2 expenses:dues
if Google
account2 expenses:online:apps
description google | music
$ hledger -f paypal-custom.csv print
2019-10-01 (60P57143A8206782E) Calm Radio MONTHLY - $1 for the first 2 Months: Me - Order 99309. Item total: $1.00 USD first 2 months, then $6.99 / Month ; itemid:, fromemail:[email protected], toemail:[email protected], time:03:46:20, type:Subscription Payment, status:Completed
assets:online:paypal $-6.99 = $-6.99
expenses:online:apps $6.99
2019-10-01 (0TU1544T080463733) Bank Deposit to PP Account for 60P57143A8206782E ; itemid:, fromemail:, toemail:[email protected], time:03:46:20, type:Bank Deposit to PP Account, status:Pending
assets:online:paypal $6.99 = $0.00
assets:bank:wf:pchecking $-6.99
2019-10-01 (2722394R5F586712G) Patreon Patreon* Membership ; itemid:, fromemail:[email protected], toemail:[email protected], time:08:57:01, type:PreApproved Payment Bill User Payment, status:Completed
assets:online:paypal $-7.00 = $-7.00
expenses:dues $7.00
2019-10-01 (71854087RG994194F) Bank Deposit to PP Account for 2722394R5F586712G Patreon* Membership ; itemid:, fromemail:, toemail:[email protected], time:08:57:01, type:Bank Deposit to PP Account, status:Pending
assets:online:paypal $7.00 = $0.00
assets:bank:wf:pchecking $-7.00
2019-10-19 (K9U43044RY432050M) Wikimedia Foundation, Inc. Monthly donation to the Wikimedia Foundation ; itemid:, fromemail:[email protected], toemail:[email protected], time:03:02:12, type:Subscription Payment, status:Completed
assets:online:paypal $-2.00 = $-2.00
expenses:dues $2.00
expenses:banking:paypal ; business:
2019-10-19 (3XJ107139A851061F) Bank Deposit to PP Account for K9U43044RY432050M ; itemid:, fromemail:, toemail:[email protected], time:03:02:12, type:Bank Deposit to PP Account, status:Pending
assets:online:paypal $2.00 = $0.00
assets:bank:wf:pchecking $-2.00
2019-10-22 (6L8L1662YP1334033) Noble Benefactor Joyful Systems ; itemid:, fromemail:[email protected], toemail:[email protected], time:05:07:06, type:Subscription Payment, status:Completed
assets:online:paypal $9.41 = $9.41
revenues:foss donations:darcshub $-10.00 ; business:
expenses:banking:paypal $0.59 ; business:
Timeclock
hledger can read time logs in the timeclock time logging format of timeclock.el. As with Ledger, hledger's timeclock format is a subset/variant of timeclock.el's.
hledger's timeclock format was updated in hledger 1.43 and 1.50. If
your old time logs are rejected, you should adapt them to modern
hledger; for now, you can restore the pre-1.43 behaviour with the
--old-timeclock flag.
Here the timeclock format in hledger 1.50+:
# Comment lines like these, and blank lines, are ignored:
# comment line
; comment line
* comment line
# Lines beginning with b, h, or capital O are also ignored, for compatibility:
b SIMPLEDATE HH:MM[:SS][+-ZZZZ][ TEXT]
h SIMPLEDATE HH:MM[:SS][+-ZZZZ][ TEXT]
O SIMPLEDATE HH:MM[:SS][+-ZZZZ][ TEXT]
# Lines beginning with i or o are are clock-in / clock-out entries:
i SIMPLEDATE HH:MM[:SS][+-ZZZZ] ACCOUNT[ DESCRIPTION][;COMMENT]]
o SIMPLEDATE HH:MM[:SS][+-ZZZZ][ ACCOUNT][;COMMENT]
The date is a hledger simple date (YYYY-MM-DD or similar). The time parts must use two digits. The seconds are optional. A + or - four-digit time zone is accepted for compatibility, but currently ignored; times are always interpreted as a local time.
In clock-in entries (i), the account name is required. A transaction
description, separated from the account name by 2+ spaces, is optional.
A transaction comment, beginning with ;, is also optional. (Indented
following comment lines are also allowed, as in journal format.)
In clock-out entries (o) have no description, but can have a comment
if you wish. A clock-in and clock-out pair form a "transaction"
posting some number of hours to an account - also known as a session.
Eg:
i 2015/03/30 09:00:00 session1
o 2015/03/30 10:00:00
$ hledger -f a.timeclock print
2015-03-30 * 09:00-10:00
(session1) 1.00h
Clock-ins and clock-outs are matched by their account/session name. If a clock-out does not specify a name, the most recent unclosed clock-in is closed. You can have multiple sessions active simultaneously. Entries are processed in the order they are parsed. Sessions spanning more than one day are automatically split at day boundaries.
Eg, the following time log:
i 2015/03/30 09:00:00 some account optional description after 2 spaces ; optional comment, tags:
o 2015/03/30 09:20:00
i 2015/03/31 22:21:45 another:account
o 2015/04/01 02:00:34
i 2015/04/02 12:00:00 another:account ; this demonstrates multple sessions being clocked in
i 2015/04/02 13:00:00 some account
o 2015/04/02 14:00:00
o 2015/04/02 15:00:00 another:account
generates these transactions:
$ hledger -f t.timeclock print
2015-03-30 * optional description after 2 spaces ; optional comment, tags:
(some account) 0.33h
2015-03-31 * 22:21-23:59
(another:account) 1.64h
2015-04-01 * 00:00-02:00
(another:account) 2.01h
2015-04-02 * 12:00-15:00 ; this demonstrates multiple sessions being clocked in
(another:account) 3.00h
2015-04-02 * 13:00-14:00
(some account) 1.00h
Here is a sample.timeclock to download and some queries to try:
$ hledger -f sample.timeclock balance # current time balances
$ hledger -f sample.timeclock register -p 2009/3 # sessions in march 2009
$ hledger -f sample.timeclock register -p weekly --depth 1 --empty # time summary by week
To generate time logs, ie to clock in and clock out, you could:
-
use these shell aliases at the command line:
alias ti='echo i `date "+%Y-%m-%d %H:%M:%S"` $* >>$TIMELOG' alias to='echo o `date "+%Y-%m-%d %H:%M:%S"` >>$TIMELOG' -
or Emacs's built-in timeclock.el, or the extended timeclock-x.el, and perhaps the extras in ledgerutils.el
-
or use the old
tiandtoscripts in the ledger 2.x repository. These rely on a "timeclock" executable which I think is just the ledger 2 executable renamed.
Timedot
timedot format is hledger's human-friendly time logging format.
Compared to timeclock format, it is more convenient for
quick, approximate, and retroactive time logging, and more
human-readable (you can see at a glance where time was spent). A quick
example:
2023-05-01
hom:errands .... .... ; two hours; the space is ignored
fos:hledger:timedot .. ; half an hour
per:admin:finance ; no time spent yet
hledger reads this as a transaction on this day with three (unbalanced) postings, where each dot represents "0.25". No commodity symbol is assumed, but we typically interpret it as hours.
$ hledger -f a.timedot print # .timedot file extension (or timedot: prefix) is required
2023-05-01 *
(hom:errands) 2.00 ; two hours
(fos:hledger:timedot) 0.50 ; half an hour
(per:admin:finance) 0
A timedot file contains a series of transactions (usually one per day). Each begins with a simple date (Y-M-D, Y/M/D, or Y.M.D), optionally be followed on the same line by a transaction description, and/or a transaction comment following a semicolon.
After the date line are zero or more time postings, consisting of:
-
An account name - any hledger-style account name, optionally indented.
-
Two or more spaces - required if there is an amount (as in journal format).
-
A timedot amount, which can be
-
empty (representing zero)
-
a number, optionally followed by a unit
s,m,h,d,w,mo, ory, representing a precise number of seconds, minutes, hours, days weeks, months or years (hours is assumed by default), which will be converted to hours according to 60s = 1m, 60m = 1h, 24h = 1d, 7d = 1w, 30d = 1mo, 365d = 1y. -
one or more dots (period characters), each representing 0.25. These are the dots in "timedot". Spaces are ignored and can be used for grouping/alignment.
-
Added in 1.32 one or more letters. These are like dots but they also generate a tag
t:(short for "type") with the letter as its value, and a separate posting for each of the values. This provides a second dimension of categorisation, viewable in reports with--pivot t.
-
-
An optional comment following a semicolon (a hledger-style posting comment).
There is some flexibility to help with keeping time log data and notes in the same file:
-
Blank lines and lines beginning with
#or;are ignored. -
After the first date line, lines which do not contain a double space are parsed as postings with zero amount. (hledger's register reports will show these if you add -E).
-
Before the first date line, lines beginning with
*(eg org headings) are ignored. And from the first date line onward, Emacs org mode heading prefixes at the start of lines (one or more*'s followed by a space) will be ignored. This means the time log can also be a org outline.
Timedot files don't support directives like journal files. So a common
pattern is to have a main journal file (eg time.journal) that contains
any needed directives, and then includes the
timedot file (include time.timedot).
Timedot examples
Numbers:
2016/2/3
inc:client1 4
fos:hledger 3h
biz:research 60m
Dots:
# on this day, 6h was spent on client work, 1.5h on haskell FOSS work, etc.
2016/2/1
inc:client1 .... .... .... .... .... ....
fos:haskell .... ..
biz:research .
2016/2/2
inc:client1 .... ....
biz:research .
$ hledger -f a.timedot print date:2016/2/2
2016-02-02 *
(inc:client1) 2.00
2016-02-02 *
(biz:research) 0.25
$ hledger -f a.timedot bal --daily --tree
Balance changes in 2016-02-01-2016-02-03:
|| 2016-02-01d 2016-02-02d 2016-02-03d
============++========================================
biz || 0.25 0.25 1.00
research || 0.25 0.25 1.00
fos || 1.50 0 3.00
haskell || 1.50 0 0
hledger || 0 0 3.00
inc || 6.00 2.00 4.00
client1 || 6.00 2.00 4.00
------------++----------------------------------------
|| 7.75 2.25 8.00
Letters:
# Activity types:
# c cleanup/catchup/repair
# e enhancement
# s support
# l learning/research
2023-11-01
work:adm ccecces
$ hledger -f a.timedot print
2023-11-01
(work:adm) 1 ; t:c
(work:adm) 0.5 ; t:e
(work:adm) 0.25 ; t:s
$ hledger -f a.timedot bal
1.75 work:adm
--------------------
1.75
$ hledger -f a.timedot bal --pivot t
1.00 c
0.50 e
0.25 s
--------------------
1.75
Org:
* 2023 Work Diary
** Q1
*** 2023-02-29
**** DONE
0700 yoga
**** UNPLANNED
**** BEGUN
hom:chores
cleaning ...
water plants
outdoor - one full watering can
indoor - light watering
**** TODO
adm:planning: trip
*** LATER
Using . as account name separator:
2016/2/4
fos.hledger.timedot 4h
fos.ledger ..
$ hledger -f a.timedot --alias '/\./=:' bal -t
4.50 fos
4.00 hledger:timedot
0.50 ledger
--------------------
4.50
PART 3: REPORTING CONCEPTS
Time periods
Report start & end date
Most hledger reports will by default show the full time period represented by the journal. The report start date will be the earliest transaction or posting date, and the report end date will be the latest transaction, posting, or market price date.
Often you will want to see a shorter period, such as the current month.
You can specify a start and/or end date with the
-b/--begin,
-e/--end, or
-p/--period options, or a date:
query argument, described below. All of these accept the smart
date syntax, also described below.
End dates are exclusive; specify the day after the last day you want to see in the report.
When dates are specified by multiple options, the last (right-most)
option wins. And when date: queries and date options are combined, the
report period will be their intersection.
Examples:
-b 2016/3/17- beginning on St. Patrick’s day 2016
-e 12/1- ending at the start of December 1st in the current year
-p 'this month'- during the current month
-p thismonth- same as above, spaces are optional
-b 2023- beginning on the first day of 2023
date:2023..ordate:2023-- same as above
-b 2024 -e 2025 -p '2000 to 2030' date:2020-01 date:2020 :
during January 2020 (the smallest common period, with the -p overriding
-b and -e)
Smart dates
In hledger's user interfaces (though not in the journal file), you can optionally use "smart date" syntax. Smart dates can be written with english words, can be relative, and can have parts omitted. Missing parts are inferred as 1, when needed. Smart dates can be interpreted as dates or periods depending on the context.
Examples:
2004-01-01, 2004/10/1, 2004.9.1, 20240504, 2024Q1 :
Exact dates. The year must have at least four digits, the month must be
1-12, the day must be 1-31, the separator can be - or / or . or
nothing. The q can be upper or lower case and the quarter number must be
1-4.
2004-10- start of month
2004q3- start of third quarter of 2004
q3- start of third quarter of current year
2004- start of year
10/1oroctoroctober- October 1st in current year
21- 21st day in current month
yesterday, today, tomorrow- -1, 0, 1 days from today
last/this/next day/week/month/quarter/year- -1, 0, 1 periods from the current period
last/this/next tuesday- the previous occurrence of the named day, or the next occurrence after today
last/this/next february- the previous occurrence of 1st of the named month, or the next occurrence after the current month
in n days/weeks/months/quarters/years- n periods from the current period
n days/weeks/months/quarters/years ahead- n periods from the current period
n days/weeks/months/quarters/years ago- -n periods from the current period
20181201- 8 digit YYYYMMDD with valid year month and day
201812- 6 digit YYYYMM with valid year and month
Dates with no separators are allowed but might give surprising results if mistyped:
20181301(YYYYMMDD with an invalid month) is parsed as an eight-digit year20181232(YYYYMMDD with an invalid day) gives a parse error201801012(a valid YYYYMMDD followed by additional digits) gives a parse error
The meaning of relative dates depends on today's date. If you need to
test or reproduce old reports, you can use the --today option to
override that. (Except for periodic transaction rules, which are not
affected by --today.)
Report intervals
A report interval can be specified so that reports like register, balance or activity become multi-period, showing each subperiod as a separate row or column.
The following standard intervals can be enabled with command-line flags:
-D/--daily-W/--weekly-M/--monthly-Q/--quarterly-Y/--yearly
More complex intervals can be specified using -p/--period, described
below.
Date adjustments
Start date adjustment
If you let hledger infer a report's start date, it will adjust the date to the previous natural boundary of the report interval, for convenient periodic reports. (If you don't want that, specify a start date.)
For example, if the journal's first transaction is on january 10th,
hledger register(no report interval) will start the report on january 10th.hledger register --monthlywill start the report on the previous month boundary, january 1st.hledger register --monthly --begin 1/5will start the report on january 5th [1].
Also if you are generating transactions or budget goals with periodic transaction rules, their start date may be adjusted in a similar way (in certain situations).
End date adjustment
A report's end date is always adjusted to include a whole number of intervals, so that the last subperiod has the same length as the others.
For example, if the journal's last transaction is on february 20th,
hledger registerwill end the report on february 20th.hledger register --monthlywill end the report at the end of february.hledger register --monthly --end 2/14also will end the report at the end of february (overriding the requested end date).hledger register --monthly --begin 1/5 --end 2/14will end the report on march 4th [1].
[1] Since hledger 1.29.
Period headings
With non-standard subperiods, hledger will show "STARTDATE..ENDDATE" headings. With standard subperiods (ie, starting on a natural interval boundary), you'll see more compact headings, which are usually preferable. (Though month names will be in english, currently.)
So if you are specifying a start date and you want compact headings:
choose a start of year for yearly reports, a start of quarter for
quarterly reports, a start of month for monthly reports, etc. (Remember,
you can write eg -b 2024 or 1/1 as a shortcut for a start of year,
or 2024-04 or 202404 or Apr for a start of month or quarter.)
For weekly reports, choose a date that's a Monday. (You can try
different dates until you see the short headings, or write eg
-b '3 weeks ago'.)
Period expressions
The -p/--period option specifies a period expression, which is a
compact way of expressing a start date, end date, and/or report
interval.
Here's a period expression with a start and end date (specifying the first quarter of 2009):
-p "from 2009/1/1 to 2009/4/1" |
Several keywords like "from" and "to" are supported for readability; these are optional. "to" can also be written as ".." or "-". The spaces are also optional, as long as you don't run two dates together. So the following are equivalent to the above:
-p "2009/1/1 2009/4/1" |
-p2009/1/1to2009/4/1 |
-p2009/1/1..2009/4/1 |
Dates are smart dates, so if the current year is 2009, these are also equivalent to the above:
-p "1/1 4/1" |
-p "jan-apr" |
-p "this year to 4/1" |
If you specify only one date, the missing start or end date will be the earliest or latest transaction date in the journal:
-p "from 2009/1/1" | everything after january 1, 2009 |
-p "since 2009/1" | the same, since is a synonym |
-p "from 2009" | the same |
-p "to 2009" | everything before january 1, 2009 |
You can also specify a period by writing a single partial or full date:
-p "2009" | the year 2009; equivalent to “2009/1/1 to 2010/1/1” |
-p "2009/1" | the month of january 2009; equivalent to “2009/1/1 to 2009/2/1” |
-p "2009/1/1" | the first day of 2009; equivalent to “2009/1/1 to 2009/1/2” |
or by using the "Q" quarter-year syntax (case insensitive):
-p "2009Q1" | first quarter of 2009, equivalent to “2009/1/1 to 2009/4/1” |
-p "q4" | fourth quarter of the current year |
Period expressions with a report interval
A period expression can also begin with a report
interval, separated from the start/end dates (if
any) by a space or the word in:
-p "weekly from 2009/1/1 to 2009/4/1" |
-p "monthly in 2008" |
-p "quarterly" |
More complex report intervals
Some more complex intervals can be specified within period expressions, such as:
biweekly(every two weeks)fortnightlybimonthly(every two months)every day|week|month|quarter|yearevery N days|weeks|months|quarters|years
Weekly on a custom day:
every Nth day of week(th,nd,rd, orstare all accepted after the number)every WEEKDAYNAME(full or three-letter english weekday name, case insensitive)
Monthly on a custom day:
every Nth day [of month](31st daywill be adjusted to each month's last day)every Nth WEEKDAYNAME [of month]
Yearly on a custom month and day:
every MM/DD [of year](month number and day of month number)every MONTHNAME DDth [of year](full or three-letter english month name, case insensitive, and day of month number)every DDth MONTHNAME [of year](equivalent to the above)
Examples:
-p "bimonthly from 2008" | |
-p "every 2 weeks" | |
-p "every 5 months from 2009/03" | |
-p "every 2nd day of week" | periods will go from Tue to Tue |
-p "every Tue" | same |
-p "every 15th day" | period boundaries will be on 15th of each month |
-p "every 2nd Monday" | period boundaries will be on second Monday of each month |
-p "every 11/05" | yearly periods with boundaries on 5th of November |
-p "every 5th November" | same |
-p "every Nov 5th" | same |
Show historical balances at end of the 15th day of each month (N is an end date, exclusive as always):
$ hledger balance -H -p "every 16th day"
Group postings from the start of wednesday to end of the following tuesday (N is both (inclusive) start date and (exclusive) end date):
$ hledger register checking -p "every 3rd day of week"
Multiple weekday intervals
This special form is also supported:
every WEEKDAYNAME,WEEKDAYNAME,...(full or three-letter english weekday names, case insensitive)
Also, weekday and weekendday are shorthand for mon,tue,wed,thu,fri
and sat,sun.
This is mainly intended for use with --forecast, to generate periodic
transactions on arbitrary days of the week. It
may be less useful with -p, since it divides each week into subperiods
of unequal length, which is unusual. (Related:
#1632)
Examples:
-p "every mon,wed,fri" | dates will be Mon, Wed, Fri; periods will be Mon-Tue, Wed-Thu, Fri-Sun |
-p "every weekday" | dates will be Mon, Tue, Wed, Thu, Fri; periods will be Mon, Tue, Wed, Thu, Fri-Sun |
-p "every weekendday" | dates will be Sat, Sun; periods will be Sat, Sun-Fri |
Depth
With the --depth NUM option (short form, usually preferred: -NUM),
reports will show accounts only to the specified depth, hiding deeper
subaccounts. Use this when you want a summary with less detail. This
flag has the same effect as a depth: query argument. So all of these
are equivalent: depth:2, --depth=2, -2.
You can also provide custom depths for specific accounts, by providing a
REGEX=NUM argument instead of just NUM (since 1.41). For example,
--depth assets=2 (or depth:assets=2) will collapse accounts matching
the regular expression "assets" to depth 2. So assets:bank:savings
would be collapsed to assets:bank, but liabilities:bank:credit card
would not be affected.
If REGEX contains spaces or other special characters, enclose it in
quotes in the usual way. Eg:
--depth 'credit card=2'
Combining depth options
If a command line contains multiple general depth options, the last one wins. (Useful for overriding a depth specified by scripts.)
Or a command may contain a combination of general and custom depth options. In this case, the most specifically (deepest) matching option wins. Some examples:
-
--depth assets=3 --depth expenses=2 --depth 1would collapse accounts containing "assets" to depth 3, accounts containing "expenses" to depth 2, and all other accounts to depth 1. -
--depth assets=1 --depth savings=2would collapseassets:bank:savingsto depth 2 (not depth 1; because "savings" matches a deeper part of the account name than "assets").
Note currently, to override a custom depth option --depth REGEX=NUM
with a later option, the later option must use the same REGEX.
Queries
Many hledger commands accept query arguments, which restrict their scope and let you report on a precise subset of your data. Here's a quick overview of hledger's queries:
-
By default, a query argument is treated as a case-insensitive substring pattern for matching account names. Eg:
dining groceriescar:fuel\ -
Patterns containing spaces or other special characters must be enclosed in single or double quotes:
'personal care'\ -
Patterns are actually regular expressions, so you can add regexp metacharacters for more precision (or you may need to backslash-escape certain characters; see "Regular expressions" above):
'^expenses\b''food$''fuel|repair''accounts (payable|receivable)'\ -
To match something other than the account name, you can add a query type prefix, such as:
date:202312-status:desc:amazoncur:USDcur:\\$amt:'>0'acct:groceries(butacct:is the default, so we usually don't bother writing it)\ -
To negate a query, add a
not:prefix:not:status:'*'not:desc:'opening|closing'not:cur:USD\ -
Multiple query terms can be combined, as space-separated queries Eg:
hledger print date:2022 desc:amazon desc:amzn(show transactions dated in 2022 whose description contains "amazon" or "amzn").\ -
Or more flexibly as boolean queries. Eg:
hledger print expr:'date:2022 and (desc:amazon or desc:amzn) and not date:202210'\
All hledger commands use the same query language, but different commands may interpret the query in different ways. We haven't described the commands yet (that's coming in PART 4: COMMANDS below) but here's the gist of it:
-
Transaction-oriented commands (
print,aregister,close,import,descriptions..) try to match transactions (including the transaction's postings). -
Posting-oriented commands (
register,balance,balancesheet,incomestatement,accounts..) try to match postings. Postings inherit their transaction's attributes for querying purposes, so transaction fields like date or description can still be referenced in a posting query. -
A few commands match in more specific ways. (Eg
aregister, which has a special first argument.)
Query types
Here are the query types available:
acct: query
acct:REGEX, or just REGEX
Match account names containing this case insensitive regular
expression.
This is the default query type, so we usually don't bother writing the
"acct:" prefix.
amt: query
amt:N, amt:'<N', amt:'<=N', amt:'>N', amt:'>=N'
Match postings with a single-commodity amount equal to, less than, or
greater than N. (Postings with multi-commodity amounts are not tested
and will always match.) amt: needs quotes to hide the less
than/greater than sign from the command line shell.
The comparison has two modes: if N is preceded by a + or - sign (or is 0), the two signed numbers are compared. Otherwise, the absolute magnitudes are compared, ignoring sign.
Keep in mind that amt: matches posting amounts, not account balances.
code: query
code:REGEX
Match by transaction code (eg check number).
cur: query
cur:REGEX
Match postings or transactions including any amounts whose
currency/commodity symbol is fully matched by REGEX. (Contrary to
hledger's usual infix matching. To do infix matching, write
.*REGEX.*.) Note, to match special characters
which are regex-significant, you need to escape them with \. And for
characters which are significant to your shell you will usually need one
more level of escaping. Eg to match the dollar sign: cur:\\$ or
cur:'\$'
desc: query
desc:REGEX
Match transaction descriptions.
date: query
date:PERIODEXPR
Match dates (or with the --date2 flag, secondary
dates) within the specified period. PERIODEXPR is a
period expression. Examples:date:2016, date:thismonth, date:2/1-2/15,
date:2021-07-27..nextquarter.
PERIODEXPR may include a report interval (since 1.52). On the command line, this is equivalent to specifying a report interval with a command line option. In other contexts (hledger-ui, hledger-web), the report interval may be ignored.
date2: query
date2:PERIODEXPR
If you use secondary dates: this matches secondary dates within the
specified period. It is not affected by the --date2 flag. A report
interval in PERIODEXPR will be ignored.
depth: query
depth:[REGEXP=]N
Match (or display, depending on command) accounts at or above this
depth, optionally only for accounts matching a provided regular
expression. See Depth for detailed rules.
note: query
note:REGEX
Match transaction notes (the part of the description
right of |, or the whole description if there's no |).
payee: query
payee:REGEX
Match transaction payee/payer names (the part of the
description left of |, or the whole description if there's no |).
real: query
real:, real:0
Match real or virtual postings respectively.
status: query
status:, status:!, status:*
Match unmarked, pending, or cleared transactions respectively.
type: query
type:TYPECODES
Match by account type (see Declaring accounts > Account
types). TYPECODES is one or more of the single-letter
account type codes ALERXCVG, case insensitive. Note type:A,
type:E, and type:R will also match their respective subtypes C
(Cash), V (Conversion), and G (Gain). Certain kinds of account alias
can disrupt account types, see Rewriting accounts > Aliases and
account types.
tag: query
tag:NAMEREGEX[=VALREGEX]
Match by tag name, and optionally also by tag value. Note:
- Both regular expressions do infix matching. If you need a complete
match, use
^and$.
Eg:tag:'^fullname$',tag:'^fullname$=^fullvalue$ - To match values, ignoring names, do
tag:.=VALREGEX - Accounts also inherit the tags of their parent accounts.
- Postings also inherit the tags of their account and their transaction .
- Transactions also acquire the tags of their postings.
Negative queries
not: query
not:QUERY
You can prepend not: to a query to negate the match.
Eg: not:equity, not:desc:apple
(Also, a trick: not:not:... can sometimes solve query problems
conveniently.)
Space-separated queries
When given multiple space-separated query terms, most commands select things which match:
- any of the description terms AND
- any of the account terms AND
- any of the status terms AND
- all the other terms.
The print command is a little different, showing transactions which:
- match any of the description terms AND
- have any postings matching any of the positive account terms AND
- have no postings matching any of the negative account terms AND
- match all the other terms.
Boolean queries
You can write more complicated "boolean" query expressions, enclosed
in quotes and prefixed with expr:. These can combine subqueries with
NOT, AND, OR operators (case insensitive), and parentheses for grouping.
Eg, to show transactions involving both cash and expense accounts:
hledger print expr:'cash AND expenses'
The prefix and enclosing quotes are required, so don't write
hledger print cash AND expenses. That would be a space-separated
query showing transactions involving accounts
with any of "cash", "and", "expenses" in their names.
You can write space-separated queries inside a boolean query, and they will combine as described above, but it might be confusing and best avoided. Eg these are equivalent, showing transactions involving cash or expenses accounts:
hledger print expr:'cash expenses'
hledger print cash expenses
There is a restriction with date: queries: they may not be used inside
OR expressions.
Actually, there are three types of boolean query: expr: for general
use, and any: and all: variants which can be useful with print.
expr: query
expr:'QUERYEXPR'
For example, expr:'date:lastmonth AND NOT (food OR rent)' means
"match things which are dated in the last month and do not have food or
rent in the account name".
When using expr: with transaction-oriented commands like print,
posting-oriented query terms like acct: and amt: are considered to
match the transaction if they match any of its postings.
So, hledger print expr:'cash and amt:>0' means "show transactions
with (at least one posting involving a cash account) and (at least one
posting with a positive amount)".
any: query
any:'QUERYEXPR'
Like expr:, but when used with transaction-oriented commands like
print, it matches the transaction only if a posting can be matched by
all of QUERYEXPR.
So, hledger print any:'cash and amt:>0' means "show transactions
where at least one posting posts a positive amount to a cash account".
all: query
all:'QUERYEXPR'
Like expr:, but when used with transaction-oriented commands like
print, it matches the transaction only if all postings are matched by
all of QUERYEXPR (and there is at least one posting).
So, hledger print all:'cash and amt:0' means "show transactions where
all postings involve a cash account and have a zero amount".
Or, hledger print all:'cash or checking' means "show transactions
which touch only cash and/or checking accounts".
Queries and command options
Some queries can also be expressed as command-line options: depth:2 is
equivalent to --depth 2, date:2023 is equivalent to -p 2023, etc.
When you mix command options and query arguments, generally the
resulting query is their intersection.
Queries and account aliases
When account names are rewritten with --alias or
alias, acct: will match either the old or the new account name.
Queries and valuation
When amounts are converted to other commodities in
cost or value reports, cur: and
amt: match the old commodity symbol and the old amount quantity, not
the new ones. (Except in hledger 1.22,
#1625.)
Pivoting
Normally, hledger groups amounts and displays their totals by account
(name). With --pivot PIVOTEXPR, some other field's (or multiple
fields') value is used as a synthetic account name, causing different
grouping and display. PIVOTEXPR can be
- any of these standard transaction or posting fields (their value is
substituted):
status,code,desc,payee,note,acct,comm/cur,amt,cost - or a tag name
- or any combination of these, colon-separated.
Some special cases:
- Colons appearing in PIVOTEXPR or in a pivoted tag value will generate account hierarchy.
- When pivoting a posting that has multiple values for a tag, the tag's first value will be used as the pivoted value.
- When a posting has multiple commodities, the pivoted value of "comm"/"cur" will be "". Also when an unrecognised tag name or field is provided, its pivoted value will be "". (If this causes confusing output, consider excluding those postings from the report.)
Examples:
2016/02/16 Yearly Dues Payment
assets:bank account 2 EUR
income:dues -2 EUR ; member: John Doe, kind: Lifetime
Normal balance report showing account names:
$ hledger balance
2 EUR assets:bank account
-2 EUR income:dues
--------------------
0
Pivoted balance report, using member: tag values instead:
$ hledger balance --pivot member
2 EUR
-2 EUR John Doe
--------------------
0
One way to show only amounts with a member: value (using a query):
$ hledger balance --pivot member tag:member=.
-2 EUR John Doe
--------------------
-2 EUR
Another way (the acct: query matches against the pivoted "account name"):
$ hledger balance --pivot member acct:.
-2 EUR John Doe
--------------------
-2 EUR
Hierarchical reports can be generated with multiple pivot values:
$ hledger balance Income:Dues --pivot kind:member
-2 EUR Lifetime:John Doe
--------------------
-2 EUR
Generating data
hledger can enrich the data provided to it, or generate new data, in a number of ways. Mostly, this is done only if you request it:
- Missing amounts or missing costs in transactions are inferred automatically when possible.
- The
--infer-equityflag infers missing conversion equity postings from @/@@ costs. - The
--infer-costsflag infers missing costs from conversion equity postings. - The
--infer-market-pricesflag infersPprice directives from costs. - The
--autoflag adds extra postings to transactions matched by auto posting rules. - The
--forecastoption generates transactions from periodic transaction rules. - The
balance --budgetreport infers budget goals from periodic transaction rules. - Commands like
close,rewrite, andhledger-interestgenerate transactions or postings. - CSV data is converted to transactions by applying CSV conversion rules.. etc.
Such generated data is temporary, existing only at report time. You can
convert it to permanent recorded data by, eg, capturing the output of
hledger print and saving it in your journal file. This can sometimes
be useful as a data entry aid.
If you are curious what data is being generated and why, run
hledger print -x --verbose-tags. -x/--explicit shows inferred
amounts and --verbose-tags adds tags like generated-transaction
(from periodic rules) and generated-posting, modified (from auto
posting rules). Similar hidden tags (with an underscore prefix) are
always present, also, so you can always match such data with queries
like tag:generated or tag:modified.
Forecasting
Forecasting, or speculative future reporting, can be useful for estimating future balances, or for exploring different future scenarios.
The simplest and most flexible way to do it with hledger is to manually
record a bunch of future-dated transactions. You could keep these in a
separate future.journal and include that with -f only when you want
to see them.
--forecast
There is another way: with the --forecast option, hledger can generate
temporary "forecast transactions" for reporting purposes, according to
periodic transaction rules defined in the
journal. Each rule can generate multiple recurring transactions, so by
changing one rule you can change many forecasted transactions.
Forecast transactions usually start after ordinary transactions end. By default, they begin after your latest-dated ordinary transaction, or today, whichever is later, and they end six months from today. (The exact rules are a little more complicated, and are given below.)
This is the "forecast period", which need not be the same as the
report period. You can override it - eg to forecast
farther into the future, or to force forecast transactions to overlap
your ordinary transactions - by giving the --forecast option a period
expression argument, like --forecast=..2099 or
--forecast=2023-02-15... Note that the = is required.
Inspecting forecast transactions
print is the best command for inspecting and troubleshooting forecast
transactions. Eg:
~ monthly from 2022-12-20 rent
assets:bank:checking
expenses:rent $1000
$ hledger print --forecast --today=2023/4/21
2023-05-20 rent
; generated-transaction: ~ monthly from 2022-12-20
assets:bank:checking
expenses:rent $1000
2023-06-20 rent
; generated-transaction: ~ monthly from 2022-12-20
assets:bank:checking
expenses:rent $1000
2023-07-20 rent
; generated-transaction: ~ monthly from 2022-12-20
assets:bank:checking
expenses:rent $1000
2023-08-20 rent
; generated-transaction: ~ monthly from 2022-12-20
assets:bank:checking
expenses:rent $1000
2023-09-20 rent
; generated-transaction: ~ monthly from 2022-12-20
assets:bank:checking
expenses:rent $1000
Here there are no ordinary transactions, so the forecasted transactions
begin on the first occurrence after today's date. (You won't normally
use --today; it's just to make these examples reproducible.)
Forecast reports
Forecast transactions affect all reports, as you would expect. Eg:
$ hledger areg rent --forecast --today=2023/4/21
Transactions in expenses:rent and subaccounts:
2023-05-20 rent as:ba:checking $1000 $1000
2023-06-20 rent as:ba:checking $1000 $2000
2023-07-20 rent as:ba:checking $1000 $3000
2023-08-20 rent as:ba:checking $1000 $4000
2023-09-20 rent as:ba:checking $1000 $5000
$ hledger bal -M expenses --forecast --today=2023/4/21
Balance changes in 2023-05-01..2023-09-30:
|| May Jun Jul Aug Sep
===============++===================================
expenses:rent || $1000 $1000 $1000 $1000 $1000
---------------++-----------------------------------
|| $1000 $1000 $1000 $1000 $1000
Forecast tags
Forecast transactions generated by --forecast have a hidden tag,
_generated-transaction. So if you ever need to match forecast
transactions, you could use tag:_generated-transaction (or just
tag:generated) in a query.
For troubleshooting, you can add the --verbose-tags flag. Then,
visible generated-transaction tags will be added also, so you can view
them with the print command. Their value indicates which periodic rule
was responsible.
Forecast period, in detail
Forecast start/end dates are chosen so as to do something useful by default in almost all situations, while also being flexible. Here are (with luck) the exact rules, to help with troubleshooting:
The forecast period starts on:
- the later of
- the start date in the periodic transaction rule
- the start date in
--forecast's argument
- otherwise (if those are not available): the later of
- the report start date specified with
-b/-p/date: - the day after the latest ordinary transaction in the journal
- the report start date specified with
- otherwise (if none of these are available): today.
The forecast period ends on:
- the earlier of
- the end date in the periodic transaction rule
- the end date in
--forecast's argument
- otherwise: the report end date specified with
-e/-p/date: - otherwise: 180 days (~6 months) from today.
Forecast troubleshooting
When --forecast is not doing what you expect, one of these tips should help:
- Remember to use the
--forecastoption. - Remember to have at least one periodic transaction rule in your journal.
- Test with
print --forecast. - Check for typos or too-restrictive start/end dates in your periodic transaction rule.
- Leave at least 2 spaces between the rule's period expression and description fields.
- Check for future-dated ordinary transactions suppressing forecasted transactions.
- Try setting explicit report start and/or end dates with
-b,-e,-pordate: - Try adding the
-Eflag to encourage display of empty periods/zero transactions. - Try setting explicit forecast start and/or end dates with
--forecast=START..END - Consult Forecast period, in detail, above.
- Check inside the engine: add
--debug=2(eg).
Budgeting
With the balance command's --budget report, each
periodic transaction rule generates recurring budget goals in specified
accounts, and goals and actual performance can be compared. See the
balance command's doc below.
You can generate budget goals and forecast transactions at the same
time, from the same or different periodic transaction rules:
hledger bal -M --budget --forecast ...
See also: Budgeting and Forecasting.
Amount formatting
Commodity display style
For the amounts in each commodity, hledger chooses a consistent display style (symbol placement, decimal mark and digit group marks, number of decimal digits) to use in most reports. This is inferred as follows:
First, if there's a D directive declaring a default
commodity, that commodity symbol and amount format is applied to all
no-symbol amounts in the journal.
Then each commodity's display style is determined from its commodity
directive. We recommend always declaring
commodities with commodity directives, since they help ensure
consistent display styles and precisions, and bring other benefits such
as error checking for commodity symbols. Here's an example:
# Set display styles (and decimal marks, for parsing, if there is no decimal-mark directive)
# for the $, EUR, INR and no-symbol commodities:
commodity $1,000.00
commodity EUR 1.000,00
commodity INR 9,99,99,999.00
commodity 1 000 000.9455
But for convenience, if a commodity directive is not present, hledger
infers a commodity's display styles from its amounts as they are
written in the journal (excluding cost amounts and amounts in periodic
transaction rules or auto posting rules). It uses
- the symbol placement and decimal mark of the first amount seen
- the digit group marks of the first amount with digit group marks
- and the maximum number of decimal digits seen across all amounts.
And as fallback if no applicable amounts are found, it would use a
default style, like $1000.00 (symbol on the left with no space, period
as decimal mark, and two decimal digits).
Finally, commodity styles can be overridden by the
-c/--commodity-style command line option.
Rounding
Amounts are stored internally as decimal numbers with up to 255 decimal places. They are displayed with their original journal precisions by print and print-like reports, and rounded to their display precision (the number of decimal digits specified by the commodity display style) by other reports. When rounding, hledger uses banker's rounding (it rounds to the nearest even digit). So eg 0.5 displayed with zero decimal digits appears as "0".
Trailing decimal marks
If you're wondering why your print report sometimes shows
trailing decimal marks, with no decimal digits; it does this when
showing amounts that have digit group marks but no decimal digits, to
disambiguate them and allow them to be re-parsed reliably (see Decimal
marks). Eg:
commodity $1,000.00
2023-01-02
(a) $1000
$ hledger print
2023-01-02
(a) $1,000.
If this is a problem (eg when exporting to Ledger), you can avoid it by disabling digit group marks, eg with -c/--commodity (for each affected commodity):
$ hledger print -c '$1000.00'
2023-01-02
(a) $1000
or by forcing print to always show decimal digits, with --round:
$ hledger print -c '$1,000.00' --round=soft
2023-01-02
(a) $1,000.00
Amount parseability
More generally, hledger output falls into three rough categories, which format amounts a little bit differently to suit different consumers:
1. "hledger-readable output" - should be readable by hledger (and by humans)
- This is produced by reports that show full journal entries:
print,import,close,rewriteetc. - It shows amounts with their original journal precisions, which may not be consistent from one amount to the next.
- It adds a trailing decimal mark when needed to avoid showing ambiguous amounts.
- It can be parsed reliably (by hledger and ledger2beancount at least, but perhaps not by Ledger..)
2. "human-readable output" - usually for humans
- This is produced by all other reports.
- It shows amounts with standard display precisions, which will be consistent within each commodity.
- It shows ambiguous amounts unmodified.
- It can be parsed reliably in the context of a known report (when you know decimals are consistently not being shown, you can assume a single mark is a digit group mark).
3. "machine-readable output" - usually for other software
- This is produced by all reports when an output format like
csv,tsv,json, orsqlis selected. - It shows amounts as 1 or 2 do, but without digit group marks.
- It can be parsed reliably (if needed, the decimal mark can be changed with -c/--commodity-style).
Cost reporting
In some transactions - for example a currency conversion, or a purchase
or sale of stock - one commodity is exchanged for another. In these
transactions there is a conversion rate, also called the cost (when
buying) or selling price (when selling). (In hledger docs we just say
"cost" generically for convenience.) With the -B/--cost flag,
hledger can show amounts "at cost", converted to the cost's
commodity.
Recording costs
We'll explore several ways of recording transactions involving costs. These are also summarised at hledger Cookbook > Cost notation.
Costs can be recorded explicitly in the journal, using the @ UNITCOST
or @@ TOTALCOST notation described in Journal > Costs:
Variant 1
2022-01-01
assets:dollars $-135
assets:euros €100 @ $1.35 ; $1.35 per euro (unit cost)
Variant 2
2022-01-01
assets:dollars $-135
assets:euros €100 @@ $135 ; $135 total cost
Typically, writing the unit cost (variant 1) is preferable; it can be more effort, requiring more attention to decimal digits; but it reveals the per-unit cost basis, and makes stock sales easier.
Costs can also be left implicit, and hledger will infer the cost that is consistent with a balanced transaction:
Variant 3
2022-01-01
assets:dollars $-135
assets:euros €100
Here, hledger will attach a @@ €100 cost to the first amount (you can
see it with hledger print -x). This form looks convenient, but there
are downsides:
-
It sacrifices some error checking. For example, if you accidentally wrote €10 instead of €100, hledger would not be able to detect the mistake.
-
It is sensitive to the order of postings - if they were reversed, a different entry would be inferred and reports would be different.
-
The per-unit cost basis is not easy to read.
So generally this kind of entry is not recommended. You can make sure
you have none of these by using -s (strict mode), or
by running hledger check balanced.
Reporting at cost
Now when you add the -B/--cost flag to reports ("B" is from
Ledger's -B/--basis/--cost flag), any amounts which have been
annotated with costs will be converted to their cost's commodity (in
the report output). Ie they will be displayed "at cost" or "at sale
price".
Some things to note:
-
Costs are attached to specific posting amounts in specific transactions, and once recorded they do not change. This contrasts with market prices, which are ambient and fluctuating.
-
Conversion to cost is performed before conversion to market value (described below).
Equity conversion postings
There is a problem with the entries above - they are not conventional
Double Entry Bookkeeping (DEB) notation, and because of the "magical"
transformation of one commodity into another, they cause an imbalance in
the Accounting Equation. This shows up as a non-zero grand total in
balance reports like hledger bse.
For most hledger users, this doesn't matter in practice and can safely be ignored ! But if you'd like to learn more, keep reading.
Conventional DEB uses an extra pair of equity postings to balance the transaction. Of course you can do this in hledger as well:
Variant 4
2022-01-01
assets:dollars $-135
assets:euros €100
equity:conversion $135
equity:conversion €-100
Now the transaction is perfectly balanced according to standard DEB, and
hledger bse's total will not be disrupted.
And, hledger can still infer the cost for cost reporting, but it's not
done by default - you must add the --infer-costs flag like so:
$ hledger print --infer-costs
2022-01-01 one hundred euros purchased at $1.35 each
assets:dollars $-135 @@ €100
assets:euros €100
equity:conversion $135
equity:conversion €-100
$ hledger bal --infer-costs -B
€-100 assets:dollars
€100 assets:euros
--------------------
0
Here are some downsides of this kind of entry:
-
The per-unit cost basis is not easy to read.
-
Instead of
-Byou must remember to type-B --infer-costs. -
--infer-costsworks only where hledger can identify the two equity:conversion postings and match them up with the two non-equity postings. So writing the journal entry in a particular format becomes more important. More on this below.
Inferring equity conversion postings
Can we go in the other direction ? Yes, if you have transactions written
with the @/@@ cost notation, hledger can infer the missing equity
postings, if you add the --infer-equity flag. Eg:
2022-01-01
assets:dollars -$135
assets:euros €100 @ $1.35
$ hledger print --infer-equity
2022-01-01
assets:dollars $-135
assets:euros €100 @ $1.35
equity:conversion:$-€:€ €-100
equity:conversion:$-€:$ $135.00
The equity account names will be "equity:conversion:A-B:A" and
"equity:conversion:A-B:B" where A is the alphabetically first
commodity symbol. You can customise the "equity:conversion" part by
declaring an account with the V/Conversion account
type.
Note you will need to add account
declarations for these to your journal, if you
use check accounts or check --strict. (And unlike normal postings,
generated equity postings do not inherit tags from account
declarations.)
Combining costs and equity conversion postings
Finally, you can use both the @/@@ cost notation and equity postings at the same time. This in theory gives the best of all worlds - preserving the accounting equation, revealing the per-unit cost basis, and providing more flexibility in how you write the entry:
Variant 5
2022-01-01 one hundred euros purchased at $1.35 each
assets:dollars $-135
equity:conversion $135
equity:conversion €-100
assets:euros €100 @ $1.35
All the other variants above can (usually) be rewritten to this final form with:
$ hledger print -x --infer-costs --infer-equity
Downsides:
-
The precise format of the journal entry becomes more important. If hledger can't detect and match up the cost and equity postings, it will give a transaction balancing error.
-
The add command does not yet accept this kind of entry (#2056).
-
This is the most verbose form.
Requirements for detecting equity conversion postings
--infer-costs has certain requirements (unlike --infer-equity, which
always works). It will infer costs only in transactions with:
-
Two non-equity postings, in different commodities. Their order is significant: the cost will be added to the first of them.
-
Two postings to equity conversion accounts, next to one another, which balance the two non-equity postings. This balancing is checked to the same precision (number of decimal places) used in the conversion posting's amount. Equity conversion accounts are:
- any accounts declared with account type
V/Conversion, or their subaccounts - otherwise, accounts named
equity:conversion,equity:trade, orequity:trading, or their subaccounts.
- any accounts declared with account type
And multiple such four-posting groups can coexist within a single
transaction. When --infer-costs fails, it does not infer a cost in
that transaction, and does not raise an error (ie, it infers costs where
it can).
Reading variant 5 journal entries, combining cost notation and equity postings, has all the same requirements. When reading such an entry fails, hledger raises an "unbalanced transaction" error.
Infer cost and equity by default ?
Should --infer-costs and --infer-equity be enabled by default ? Try
using them always, eg with a shell alias:
alias h="hledger --infer-equity --infer-costs"
and let us know what problems you find.
Value reporting
hledger can also show amounts "at market value", converted to some other commodity using the market price or conversion rate on a certain date.
This is controlled by the --value=TYPE[,COMMODITY] option. We also
provide simpler -V and -X COMMODITY aliases for this, which are
often sufficient. The market prices are declared with a special P
directive, and/or they can be inferred from the costs recorded in
transactions, by using the --infer-market-prices flag.
-X: Value in specified commodity
The -X COMM (or --exchange=COMM) option converts amounts to their
market value in the specified commodity, using the market
prices in effect on the valuation date(s), if any.
(More on these in a minute.)
Use this when you want to (eg) show everything in your base currency as far as possible. (Commodities for which no conversion rate can be found, will not be converted.)
COMM should be the full commodity symbol or name. Remember to quote special shell characters, if needed. Some examples:
-X€-X$(nothing after $, no quoting needed)-X CNY(the space after -X is optional)-X 'red apples'-X 'r&r'
-V: Value in default commodity(s)
The -V/--market flag is a variant of -X where you don't have to
specify COMM. Instead it tries to guess a default valuation commodity
for each original commodity, based on the market prices
in effect on the valuation date(s).
-V can often be a convenient shortcut for -X MYCURRENCY, but not
always; depending on your data it could guess multiple valuation
commodities. Usually you want to convert to a single commodity, so it's
better to use -X, unless you're sure -V is doing what you want.
Valuation date
Market prices can change from day to day. hledger will use the prices on a particular valuation date (or on more than one date). By default hledger uses "end" dates for valuation. More specifically:
- For single period reports (including normal print and register
reports):
- If an explicit report end date is specified, that is used.
- Otherwise the latest transaction date or non-future P directive date is used.
- For multiperiod reports, each period is valued on its last day.
This can be customised with the --value option described below, which can select either "then", "end", "now", or "custom" dates.
Finding market price
To convert a commodity A to its market value in another commodity B, hledger looks for a suitable market price (exchange rate) as follows, in this order of preference:
-
A declared market price or inferred market price: A's latest market price in B on or before the valuation date as declared by a P directive, or (with the
--infer-market-pricesflag) inferred from costs. -
A reverse market price: the inverse of a declared or inferred market price from B to A.
-
A forward chain of market prices: a synthetic price formed by combining the shortest chain of "forward" (only 1 above) market prices, leading from A to B.
-
Any chain of market prices: a chain of any market prices, including both forward and reverse prices (1 and 2 above), leading from A to B.
There is a limit to the length of these price chains; if hledger reaches
that length without finding a complete chain or exhausting all
possibilities, it will give up (with a "gave up" message visible in
--debug=2 output). That limit is currently 1000.
Amounts for which no suitable market price can be found, are not converted.
--infer-market-prices: market prices from transactions
Normally, market value in hledger is fully controlled by, and requires,
P directives in your journal. Since adding and updating
those can be a chore, and since transactions usually take place at close
to market value, why not use the recorded costs as additional
market prices (as Ledger does) ? Adding the --infer-market-prices flag
to -V, -X or --value enables this.
So for example, hledger bs -V --infer-market-prices will get market
prices both from P directives and from transactions. If both occur on
the same day, the P directive takes precedence.
There is a downside: value reports can sometimes be affected in
confusing/undesired ways by your journal entries. If this happens to
you, read all of this Value reporting section
carefully, and try adding --debug or --debug=2 to troubleshoot.
--infer-market-prices can infer market prices from:
-
multicommodity transactions with explicit prices (
@/@@) -
multicommodity transactions with implicit prices (no
@, two commodities, unbalanced). (With these, the order of postings matters.hledger print -xcan be useful for troubleshooting.) -
multicommodity transactions with equity postings, if cost is inferred with
--infer-costs.
There is a limitation (bug) currently: when a valuation commodity is not
specified, prices inferred with --infer-market-prices do not help
select a default valuation commodity, as P prices would. So conversion
might not happen because no valuation commodity was detected
(--debug=2 will show this). To be safe, specify the valuation
commmodity, eg:
-X EUR --infer-market-prices, not-V --infer-market-prices--value=then,EUR --infer-market-prices, not--value=then --infer-market-prices
Signed costs and market prices can be confusing. For reference, here is the current behaviour, since hledger 1.25. (If you think it should work differently, see #1870.)
2022-01-01 Positive Unit prices
a A 1
b B -1 @ A 1
2022-01-01 Positive Total prices
a A 1
b B -1 @@ A 1
2022-01-02 Negative unit prices
a A 1
b B 1 @ A -1
2022-01-02 Negative total prices
a A 1
b B 1 @@ A -1
2022-01-03 Double Negative unit prices
a A -1
b B -1 @ A -1
2022-01-03 Double Negative total prices
a A -1
b B -1 @@ A -1
All of the transactions above are considered balanced (and on each day, the two transactions are considered equivalent). Here are the market prices inferred for B:
$ hledger -f- --infer-market-prices prices
P 2022-01-01 B A 1
P 2022-01-01 B A 1.0
P 2022-01-02 B A -1
P 2022-01-02 B A -1.0
P 2022-01-03 B A -1
P 2022-01-03 B A -1.0
Valuation commodity
When you specify a valuation commodity (-X COMM or
--value TYPE,COMM):
hledger will convert all amounts to COMM, wherever it can find a
suitable market price (including by reversing or chaining prices).
When you leave the valuation commodity unspecified (-V or
--value TYPE):
For each commodity A, hledger picks a default valuation commodity as
follows, in this order of preference:
-
The price commodity from the latest P-declared market price for A on or before valuation date.
-
The price commodity from the latest P-declared market price for A on any date. (Allows conversion to proceed when there are inferred prices before the valuation date.)
-
If there are no P directives at all (any commodity or date) and the
--infer-market-pricesflag is used: the price commodity from the latest transaction-inferred price for A on or before valuation date.
This means:
-
If you have P directives, they determine which commodities
-Vwill convert, and to what. -
If you have no P directives, and use the
--infer-market-pricesflag, costs determine it.
Amounts for which no valuation commodity can be found are not converted.
--value: Flexible valuation
-V and -X are special cases of the more general --value option:
--value=TYPE[,COMM] TYPE is then, end, now or YYYY-MM-DD.
COMM is an optional commodity symbol.
Shows amounts converted to:
- default valuation commodity (or COMM) using market prices at posting dates
- default valuation commodity (or COMM) using market prices at period end(s)
- default valuation commodity (or COMM) using current market prices
- default valuation commodity (or COMM) using market prices at some date
The TYPE part selects cost or value and valuation date:
--value=then- Convert amounts to their value in the default valuation commodity, using market prices on each posting's date.
--value=end- Convert amounts to their value in the default valuation commodity, using market prices on the last day of the report period (or if unspecified, the journal's end date); or in multiperiod reports, market prices on the last day of each subperiod.
--value=now- Convert amounts to their value in the default valuation commodity using current market prices (as of when report is generated).
--value=YYYY-MM-DD- Convert amounts to their value in the default valuation commodity using market prices on this date.
To select a different valuation commodity, add the optional ,COMM
part: a comma, then the target commodity's symbol. Eg:
--value=now,EUR. hledger will do its best to convert amounts to
this commodity, deducing market prices as described
above.
Valuation examples
Here are some quick examples of -V:
; one euro is worth this many dollars from nov 1
P 2016/11/01 € $1.10
; purchase some euros on nov 3
2016/11/3
assets:euros €100
assets:checking
; the euro is worth fewer dollars by dec 21
P 2016/12/21 € $1.03
How many euros do I have ?
$ hledger -f t.j bal -N euros
€100 assets:euros
What are they worth at end of nov 3 ?
$ hledger -f t.j bal -N euros -V -e 2016/11/4
$110.00 assets:euros
What are they worth after 2016/12/21 ? (no report end date specified, defaults to today)
$ hledger -f t.j bal -N euros -V
$103.00 assets:euros
Here are some examples showing the effect of --value, as seen with
print:
P 2000-01-01 A 1 B
P 2000-02-01 A 2 B
P 2000-03-01 A 3 B
P 2000-04-01 A 4 B
2000-01-01
(a) 1 A @ 5 B
2000-02-01
(a) 1 A @ 6 B
2000-03-01
(a) 1 A @ 7 B
Show the cost of each posting:
$ hledger -f- print --cost
2000-01-01
(a) 5 B
2000-02-01
(a) 6 B
2000-03-01
(a) 7 B
Show the value as of the last day of the report period (2000-02-29):
$ hledger -f- print --value=end date:2000/01-2000/03
2000-01-01
(a) 2 B
2000-02-01
(a) 2 B
With no report period specified, the latest transaction date or price date is used as valuation date (2000-04-01):
$ hledger -f- print --value=end
2000-01-01
(a) 3 B
2000-02-01
(a) 3 B
2000-03-01
(a) 3 B
The value today is the same (the 2000-04-01 price is still in effect):
$ hledger -f- print --value=now
2000-01-01
(a) 4 B
2000-02-01
(a) 4 B
2000-03-01
(a) 4 B
Show the value on 2000/01/15:
$ hledger -f- print --value=2000-01-15
2000-01-01
(a) 1 B
2000-02-01
(a) 1 B
2000-03-01
(a) 1 B
Interaction of valuation and queries
When matching postings based on queries in the presence of valuation, the following happens:
- The query is separated into two parts:
- the currency (
cur:) or amount (amt:). - all other parts.
- the currency (
- The postings are matched to the currency and amount queries based on pre-valued amounts.
- Valuation is applied to the postings.
- The postings are matched to the other parts of the query based on post-valued amounts.
Related: #1625
Effect of valuation on reports
Here is a reference for how valuation is supposed to affect each part of hledger's reports. It may be useful when troubleshooting. If you find problems, please report them, ideally with a reproducible example. Related: #329, #1083.
First, a quick glossary:
- cost
- calculated using price(s) recorded in the transaction(s).
- value
- market value using available market price declarations, or the unchanged amount if no conversion rate can be found.
- report start
- the first day of the report period specified with -b or -p or date:, otherwise today.
- report or journal start
- the first day of the report period specified with -b or -p or date:, otherwise the earliest transaction date in the journal, otherwise today.
- report end
- the last day of the report period specified with -e or -p or date:, otherwise today.
- report or journal end
- the last day of the report period specified with -e or -p or date:, otherwise the latest transaction date in the journal, otherwise today.
- report interval
- a flag (-D/-W/-M/-Q/-Y) or period expression that activates the report's multi-period mode (whether showing one or many subperiods).
| Report type | -B, --cost | -V, -X | --value=then | --value=end | --value=DATE, --value=now |
|---|---|---|---|---|---|
| posting amounts | cost | value at report end or today | value at posting date | value at report or journal end | value at DATE/today |
| balance assertions/assignments | unchanged | unchanged | unchanged | unchanged | unchanged |
| register | |||||
| starting balance (-H) | cost | value at report or journal end | valued at day each historical posting was made | value at report or journal end | value at DATE/today |
| starting balance (-H) with report interval | cost | value at day before report or journal start | valued at day each historical posting was made | value at day before report or journal start | value at DATE/today |
| posting amounts | cost | value at report or journal end | value at posting date | value at report or journal end | value at DATE/today |
| summary posting amounts with report interval | summarised cost | value at period ends | sum of postings in interval, valued at interval start | value at period ends | value at DATE/today |
| running total/average | sum/average of displayed values | sum/average of displayed values | sum/average of displayed values | sum/average of displayed values | sum/average of displayed values |
| balance (bs, bse, cf, is) | |||||
| balance changes | sums of costs | value at report end or today of sums of postings | value at posting date | value at report or journal end of sums of postings | value at DATE/today of sums of postings |
| budget amounts (--budget) | like balance changes | like balance changes | like balance changes | like balances | like balance changes |
| grand total | sum of displayed values | sum of displayed values | sum of displayed valued | sum of displayed values | sum of displayed values |
| balance (bs, bse, cf, is) with report interval | |||||
| starting balances (-H) | sums of costs of postings before report start | value at report start of sums of all postings before report start | sums of values of postings before report start at respective posting dates | value at report start of sums of all postings before report start | sums of postings before report start |
| balance changes (bal, is, bs --change, cf --change) | sums of costs of postings in period | same as --value=end | sums of values of postings in period at respective posting dates | balance change in each period, valued at period ends | value at DATE/today of sums of postings |
| end balances (bal -H, is --H, bs, cf) | sums of costs of postings from before report start to period end | same as --value=end | sums of values of postings from before period start to period end at respective posting dates | period end balances, valued at period ends | value at DATE/today of sums of postings |
| budget amounts (--budget) | like balance changes/end balances | like balance changes/end balances | like balance changes/end balances | like balances | like balance changes/end balances |
| row totals, row averages (-T, -A) | sums, averages of displayed values | sums, averages of displayed values | sums, averages of displayed values | sums, averages of displayed values | sums, averages of displayed values |
| column totals | sums of displayed values | sums of displayed values | sums of displayed values | sums of displayed values | sums of displayed values |
| grand total, grand average | sum, average of column totals | sum, average of column totals | sum, average of column totals | sum, average of column totals | sum, average of column totals |
--cumulative is omitted to save space, it works like -H but with a
zero starting balance.
PART 4: COMMANDS
Here are hledger's standard subcommands. You can list
these by running hledger. If you have installed more add-on
commands, they also will be listed.
In the following command docs, each command's specific options are
shown. Most commands also support the general options
described above, though some of them might have no effect. (Usually if
there's a sensible way for a general option to affect a command, it
will.) You can list all of a command's options by running
hledger CMD -h.
- commands - show the hledger commands list (default)
- demo - show small hledger demos in the terminal
- help - show the hledger manual with info, man, or pager
- repl - run commands from an interactive prompt
- run - run commands from a script
- ui - (if installed) run hledger's terminal UI
- web - (if installed) run hledger's web UI
- add - add transactions using terminal prompts
- import - add new transactions from other files, eg CSV files
- accounts - show account names
- codes - show transaction codes
- commodities - show commodity/currency symbols
- descriptions - show transaction descriptions
- files - show input file paths
- notes - show note parts of transaction descriptions
- payees - show payee parts of transaction descriptions
- prices - show market prices
- stats - show journal statistics
- tags - show tag names
- print - show transactions or export journal data
- aregister (areg) - show transactions in a particular account
- register (reg) - show postings in one or more accounts & running total
- balancesheet (bs) - show assets, liabilities and net worth
- balancesheetequity (bse) - show assets, liabilities and equity
- cashflow (cf) - show changes in liquid assets
- incomestatement (is) - show revenues and expenses
- balance (bal) - show balance changes, end balances, budgets, gains..
- roi - show return on investments
- activity - show bar charts of posting counts per period
- close - generate balance-zeroing/restoring transactions
- rewrite - generate auto postings, like print --auto
- check - check for various kinds of error in the data
- diff - compare account transactions in two journal files
- setup - check and show the status of the hledger installation
- test - run self tests
Next, these commands are described in detail.
Help commands
commands
Show the hledger commands list.
Flags:
--builtin show only builtin commands, not addons
demo
Play demos of hledger usage in the terminal, if asciinema is installed.
Flags:
-s --speed=SPEED playback speed (1 is original speed, .5 is half, 2
is double, etc (default: 2))
Run this command with no argument to list the demos. To play a demo, write its number or a prefix or substring of its title. Tips:
Make your terminal window large enough to see the demo clearly.
Use the -s/--speed SPEED option to set your preferred playback speed,
eg -s4 to play at 4x original speed or -s.5 to play at half speed.
The default speed is 2x.
During playback, several keys are available: SPACE to pause/unpause, . to step forward (while paused), CTRL-c quit.
Examples:
$ hledger demo # list available demos
$ hledger demo 1 # play the first demo at default speed (2x)
$ hledger demo install -s4 # play the "install" demo at 4x speed
This command is experimental: there aren't many useful demos yet.
help
Show the hledger user manual with info, man, or a pager. With a
(case insensitive) TOPIC argument, try to open it at that section
heading.
Flags:
-i show the manual with info
-m show the manual with man
-p show the manual with $PAGER or less
(less is always used if TOPIC is specified)
This command shows the hledger manual built in to your hledger executable. It can be useful when offline, or when you prefer the terminal to a web browser, or when the appropriate hledger manual or viewers are not installed properly on your system.
By default it chooses the best viewer found in $PATH, trying in this
order: info, man, $PAGER, less, more, stdout. (If a TOPIC is
specified, $PAGER and more are not tried.) You can force the use of
info, man, or a pager with the -i, -m, or -p flags. If no viewer
can be found, or if running non-interactively, it just prints the manual
to stdout.
When using info, TOPIC can match either the full heading or a prefix.
If your info --version is < 6, you'll need to upgrade it, eg with
'brew install texinfo' on mac.
When using man or less, TOPIC must match the full heading. For a
prefix match, you can write 'TOPIC.*'.
Examples
$ hledger help -h # show the help command's usage
$ hledger help # show the manual with info, man or $PAGER
$ hledger help 'time periods' # show the manual's "Time periods" topic
$ hledger help 'time periods' -m # use man, even if info is installed
User interface commands
repl
Start an interactive prompt, where you can run any of hledger's commands. Data files are parsed just once, so the commands run faster.
Flags:
no command-specific flags
This command is experimental and could change in the future.
hledger repl starts a read-eval-print loop (REPL) where you can enter
commands interactively. As with the run command, each input file (or
each input file/input options combination) is parsed just once, so
commands will run more quickly than if you ran them individually at the
command line.
Also like run, the input file(s) specified for the repl command will
be the default input for all interactive commands. You can override this
temporarily by specifying an -f option in particular commands. But
note that commands will not see any changes made to input files (eg by
add) until you exit and restart the REPL.
The command syntax is the same as with run:
- enter one hledger command at a time, without the usual
hledgerfirst word - empty lines and comment text from
#to end of line are ignored - use single or double quotes to quote arguments when needed
- type
exitorquitor control-D to exit the REPL.
While it is running, the REPL remembers your command history, and you can navigate in the usual ways:
- Keypad or Emacs navigation keys to edit the current command line
- UP/DOWN or control-P/control-N to step back/forward through history
- control-R to search for a past command
- TAB to complete file paths.
Generally repl command lines should feel much like the normal hledger
CLI, but you may find differences. repl is a little stricter; eg it
requires full command names or official abbreviations (as seen in the
commands list).
The commands and help commands, and the command help flags
(CMD --tldr, CMD -h/--help, CMD --info, CMD --man), can be
useful.
You can type control-C to cancel a long-running command (but only once; typing it a second time will exit the REPL).
And in most shells you can type control-Z to temporarily exit to the
shell (and then fg to return to the REPL).
Examples
Start the REPL and enter some commands:
$ hledger repl
Enter hledger commands. To exit, enter 'quit' or 'exit', or send EOF.
% stats
Main file : .../2025.journal
...
% stats -f 2024/2024.journal
Main file : .../2024.journal
...
% stats
Main file : .../2025.journal
...
or:
$ hledger repl -f some.journal
Enter hledger commands. To exit, enter 'quit' or 'exit', or send EOF.
% bs
...
% print -b 'last week'
...
% bs -f other.journal
...
run
Run a sequence of hledger commands, provided as files or command line arguments. Data files are parsed just once, so the commands run faster.
Flags:
no command-specific flags
This command is experimental and could change in the future.
You can use run in three ways:
hledger run -- CMD1 -- CMD2 -- CMD3- read commands from the command line, separated by--hledger run SCRIPTFILE1 SCRIPTFILE2- read commands from one or more filescat SCRIPTFILE1 | hledger run- read commands from standard input.
run first loads the input file(s) specified by LEDGER_FILE or by
-f options, in the usual way. Then it runs each command in turn, each
using the same input data. But if you want a particular command to use
different input, you can specify an -f option within that command.
This will override (not add to) the default input, just for that
command.
Each input file (more precisely, each combination of input file and input options) is parsed only once. This means that commands will not see any changes made to these files, until the next run. But the commands will run more quickly than if run individually (typically about twice as fast).
Command scripts, whether in a file or written on the command line, have a simple syntax:
- each line may contain a single hledger command and its arguments,
without the usual
hledgerfirst word - empty lines are ignored
- text from
#to end of line is a comment, and ignored - you can use single or double quotes to quote arguments when needed, as on the command line
- these extra commands are available:
echo TEXTprints some text, andexitorquitends the run.
On unix systems you can use #!/usr/bin/env hledger run in the first
line of a command file to make it a runnable script. If that gives an
error, use #!/usr/bin/env -S hledger run.
It's ok to use the run command recursively within a command script.
You may find some differences in behaviour between run command lines
and normal hledger command lines. run is a little stricter; eg it
requires full command names or official abbreviations (as seen in the
commands list), and command options must be written after the command
name.
Examples
Run commands from the command line:
hledger -f some.journal run -- balance assets --depth 2 -- balance liabilities -f /some/other.journal --depth 3 --transpose -- stats
This would load some.journal, run balance assets --depth 2 on it,
then run balance liabilities --depth 3 --transpose on
/some/other.journal, and finally run stats on some.journal
Run commands from standard input:
(echo "files"; echo "stats") | hledger -f some.journal run
Run commands as a script:
$ cat report
#!/usr/bin/env -S hledger run -f some.journal
echo "List of accounts in some.journal"
accounts
echo "Assets of some.journal"
balance assets --depth 2
echo "Liabilities from /some/other.journal"
balance liabilities -f /some/other.journal --depth 3 --transpose
echo "Commands from another.script, applied to another.journal"
run -f another.journal another.script
$ chmod +x report
$ ./report
List of accounts in some.journal
...
ui
Runs hledger-ui (if installed).
web
Runs hledger-web (if installed).
Data entry commands
add
Add new transactions to a journal file, with interactive prompting.
Flags:
--no-new-accounts don't allow creating new accounts
Many hledger users edit their journals directly with a text editor, or
generate them from CSV. For more interactive data entry, there is the
add command, which prompts interactively on the console for new
transactions, and appends them to the main journal file (which should be
in journal format). Existing transactions are not changed. This is one
of the few hledger commands that writes to the journal file (see also
import).
To use it, just run hledger add and follow the prompts. You can add as
many transactions as you like; when you are finished, enter . or press
control-d or control-c to exit.
Features:
- add tries to provide useful defaults, using the most similar (by description) recent transaction (filtered by the query, if any) as a template.
- You can also set the initial defaults with command line arguments.
- Readline-style edit keys can be used during data entry.
- The tab key will auto-complete whenever possible - accounts,
payees/descriptions, dates (
yesterday,today,tomorrow). If the input area is empty, it will insert the default value. - A parenthesised transaction code may be entered following a date.
- Comments and tags may be entered following a description or amount.
- If you make a mistake, enter
<at any prompt to go one step backward. - Input prompts are displayed in a different colour when the terminal supports it.
Notes:
- If you enter a number with no commodity symbol, and you have declared
a default commodity with a
Ddirective, you might expectaddto add this symbol for you. It does not do this; we assume that if you are using aDdirective you prefer not to see the commodity symbol repeated on amounts in the journal. addcreates entries in journal format; it won't work with timeclock or timedot files.- There is a known issue on Windows if this hledger version is built
from stackage: the prompts will show ANSI junk instead of colours
(#2410). You can avoid this by using official hledger release binaries
or by building it with haskeline >=0.8.4; or by running
addwith--color=no, perhaps configured in your config file.
Examples:
-
Record new transactions, saving to the default journal file:
hledger add -
Add transactions to 2024.journal, but also load 2023.journal for completions:
hledger add --file 2024.journal --file 2023.journal -
Provide answers for the first four prompts:
hledger add today 'best buy' expenses:supplies '$20'
There is a detailed tutorial at https://hledger.org/add.html.
add and balance assertions
Since hledger 1.43, you can add a balance
assertion by writing AMOUNT = BALANCE when asked
for an amount. Eg 100 = 500.
Also, each time you enter a new amount, hledger re-checks all balance
assertions in the journal and rejects the new amount if it would make
any of them fail. You can run add with -I/--ignore-assertions to
disable balance assertion checking.
add and balance assignments
Since hledger 1.51, you can add a balance
assignment by writing = BALANCE (or ==, =*
etc) when asked for an amount. The missing amount will be calculated
automatically.
add normally won't let you add a new posting which is dated earlier
than an existing balance assignment. (Because when add runs, existing
balance assignments have already been calculated and converted to
amounts and balance assertions.) You can allow it by disabling balance
assertion checking with -I.
import
Import new transactions from one or more data files to the main journal.
Flags:
--catchup just mark all transactions as already imported
--dry-run just show the transactions to be imported
This command detects new transactions in one or more data files specified as arguments, and appends them to the main journal.
You can import from any input file format hledger supports, but CSV/SSV/TSV files, downloaded from financial institutions, are the most common import source.
The import destination is the default journal file, or another specified
in the usual way with $LEDGER_FILE or -f/--file. It should be in
journal format.
Examples:
$ hledger import bank1-checking.csv bank1-savings.csv
$ hledger import *.csv
Import dry run
It's useful to preview the import by running first with --dry-run, to
sanity check the range of dates being imported, and to check the effect
of your conversion rules if converting from CSV. Eg:
$ hledger import bank.csv --dry-run
The dry run output is valid journal format, so hledger can re-parse it. If the output is large, you could show just the uncategorised transactions like so:
$ hledger import --dry-run bank.csv | hledger -f- -I print unknown
You could also run this repeatedly to see the effect of edits to your conversion rules:
$ watchexec -- "hledger import --dry-run bank.csv | hledger -f- -I print unknown"
Once the conversion and dates look good enough to import to your journal, perhaps with some manual fixups to follow, you would do the actual import:
$ hledger import bank.csv
Overlap detection
Reading CSV files is built in to hledger, and not specific to import;
so you could also import by doing
hledger -f bank.csv print >>$LEDGER_FILE.
But import is easier and provides some advantages. The main one is
that it avoids re-importing transactions it has seen on previous runs.
This means you don't have to worry about overlapping data in successive
downloads of your bank CSV; just download and import as often as you
like, and only the new transactions will be imported each time.
We don't call this "deduplication", as it's generally not possible
to reliably detect duplicates in bank CSV. Instead, import remembers
the latest date processed previously in each CSV file (saving it in a
hidden file), and skips any records prior to that date. This works well
for most real-world CSV, where:
- the data file name is stable (does not change) across imports
- the item dates are stable across imports
- the order of same-date items is stable across imports
- the newest items have the newest dates
(Occasional violations of 2-4 are often harmless; you can reduce the chance of disruption by downloading and importing more often.)
Overlap detection is automatic, and shouldn't require much attention from you, except perhaps at first import (see below). But here's how it works:
-
For each
FILEbeing imported from:-
hledger reads a file named
.latest.FILEfile in the same directory, if any. This file contains the latest record date previously imported from FILE, in YYYY-MM-DD format. If multiple records with that date were imported, the date is repeated on N lines. -
hledger reads records from FILE. If a latest date was found in step 1, any records before that date, and the first N records on that date, are skipped.
-
-
After a successful import from all FILEs, without error and without
--dry-run, hledger updates each FILE's.latest.FILEfor next time.
If this goes wrong, it's relatively easy to repair:
- You'll notice it before import when you preview with
import --dry-run. - Or after import when you try to reconcile your hledger account balances with your bank.
hledger print -f FILE.csvwill show all recently downloaded transactions. Compare these with your journal. Copy/paste if needed.- Update your conversion rules and print again, if needed.
- You can manually update or remove the .latest file, or use
import --catchup FILE. - Download and import more often, eg twice a week, at least while you are learning. It's easier to review and troubleshoot when there are fewer transactions.
First import
The first time you import from a file, when no corresponding .latest file has been created yet, all of the records will be imported.
But perhaps you have been entering the data manually, so you know that
all of these transactions are already recorded in the journal. In this
case you can run hledger import --catchup once. This will create a
.latest file containing the latest CSV record date, so that none of
those records will be re-imported.
Or, if you know that some but not all of the transactions are in the
journal, you can create the .latest file yourself. Eg, let's say you
previously recorded foobank transactions up to 2024-10-31 in the
journal. Then in the directory where you'll be saving foobank.csv,
you would create a .latest.foobank.csv file containing
2024-10-31
Or if you had three foobank transactions recorded with that date, you would repeat the date that many times:
2024-10-31
2024-10-31
2024-10-31
Then hledger import foobank.csv [--dry-run] will import only the newer
records.
Importing balance assignments
Journal entries added by import will have all posting amounts made
explicit (like print -x).
This means that any balance assignments in the imported entries would need to be evaluated. But this generally isn't possible, as the main file's account balances are not visible during import. So try to avoid generating balance assignments with your CSV rules, or importing from a journal that contains balance assignments. (Balance assignments are best avoided anyway.)
But if you must use them, eg because your CSV includes only balances:
you can import with print, which leaves implicit amounts
implicit. (print can also do overlap detection like import, with the
--new flag):
$ hledger print --new -f bank.csv >> $LEDGER_FILE
(If you think import should preserve implicit balances, please test
that and send a pull request.)
Import and commodity styles
Amounts in entries added by import will be formatted according to the
journal's canonical commodity styles, as
declared by commodity directives or inferred
from the journal's amounts.
Related: CSV > Amount decimal places.
Import archiving
When importing from a CSV rules file (hledger import bank.rules), you
can use the archive rule to enable automatic archiving of
the data file. After a successful import, the data file (specified by
source) will be moved to an archive folder (data/, next to the rules
file, auto-created), and renamed similar to the rules file, with a date.
This can be useful for troubleshooting, detecting variations in your
banks' CSV data, regenerating entries with improved rules, etc.
The archive rule also causes import to handle source glob patterns
differently: when there are multiple matched files, it will pick the
oldest, not the newest.
Import special cases
Deduplication
Here are two kinds of "deduplication" which import does not handle
(and should not, because these can happen legitimately in financial
data):
- Two or more of the new CSV records are identical, and generate identical new journal entries.
- A new CSV record generates a journal entry identical to one(s) already in the journal.
Varying file name
If you have a download whose file name varies, you could rename it to a
fixed name after each download. Or you could use a CSV source
rule with a suitable glob pattern, and import from the .rules
file.
Multiple versions
Say you download bank.csv, import it, but forget to delete it from
your downloads folder. The next time you download it, your web browser
will save it as (eg) bank (2).csv. The source rule's glob
patterns are for just this situation: instead of specifying
source bank.csv, specify source bank*.csv. Then
hledger -f bank.rules CMD or hledger import bank.rules will
automatically pick the newest matched file (bank (2).csv).
Alternately, what if you download, but forget to import or delete, then
download again ? Now each of bank.csv and bank (2).csv might contain
data that's not in the other, and not in your journal. In this case,
it's best to import each of them in turn, oldest first (otherwise,
overlap detection could cause new records to be skipped). Enabling
import archiving ensures this. Then
hledger import bank.rules; hledger import bank.rules will import and
archive first bank.csv, then bank (2).csv.
Basic report commands
accounts
List the account names used or declared in the journal.
Flags:
-u --used list accounts used
-d --declared list accounts declared
--undeclared list accounts used but not declared
--unused list accounts declared but not used
--find list the first account matched by the first
argument (a case-insensitive infix regexp)
--directives show as account directives, for use in journals
--locations also show where accounts were declared
--types also show account types when known
-l --flat list/tree mode: show accounts as a flat list
(default)
-t --tree list/tree mode: show accounts as a tree
--drop=N flat mode: omit N leading account name parts
This command lists account names - all of them by default, or just the
ones which have been used in transactions (-u/--used), or declared
with account directives (-d/--declared), or used but not declared
(--undeclared), or declared but not used (--unused), or just the
first one matched by a pattern (--find, returning a non-zero exit code
if it fails).
You can add query arguments to select a subset of transactions or accounts.
With --directives, it shows valid account directives which could be
pasted into a journal file. This is useful together with --undeclared
when updating your account declarations to satisfy
hledger check accounts.
With --locations, it also shows the file and line number of each
account's declaration, if any, and the account's overall declaration
order; these may be useful when troubleshooting account display order.
With --types, it also shows each account's type, if it's known. (See
Declaring accounts > Account types.)
It shows a flat list by default. With --tree, it uses indentation to
show the account hierarchy. In flat mode you can add --drop N to omit
the first few account name components. Account names can be
depth-clipped with depth:N or --depth N or -N.
Examples:
$ hledger accounts
assets:bank:checking
assets:bank:saving
assets:cash
expenses:food
expenses:supplies
income:gifts
income:salary
liabilities:debts
$ hledger accounts --undeclared --directives >> $LEDGER_FILE
$ hledger check accounts
codes
List the codes seen in transactions, in the order parsed.
Flags:
no command-specific flags
This command prints the value of each transaction's code field, in the order transactions were parsed. The transaction code is an optional value written in parentheses between the date and description, often used to store a cheque number, order number or similar.
Transactions aren't required to have a code, and missing or empty codes
will not be shown by default. With the -E/--empty flag, they will be
printed as blank lines.
You can add a query to select a subset of transactions.
Examples:
2022/1/1 (123) Supermarket
Food $5.00
Checking
2022/1/2 (124) Post Office
Postage $8.32
Checking
2022/1/3 Supermarket
Food $11.23
Checking
2022/1/4 (126) Post Office
Postage $3.21
Checking
$ hledger codes
123
124
126
$ hledger codes -E
123
124
126
commodities
List the commodity symbols used or declared in the journal.
Flags:
--used list commodities used
--declared list commodities declared
--undeclared list commodities used but not declared
--unused list commodities declared but not used
--find list the first commodity matched by the first
argument (a case-insensitive infix regexp)
This command lists commodity symbols/names - all of them by default, or
just the ones which have been used in transactions or P directives, or
declared with commodity directives, or used but not declared, or
declared but not used, or just the first one matched by a pattern (with
--find, returning a non-zero exit code if it fails).
You can add cur: query arguments to further limit the
commodities.
descriptions
List the unique descriptions used in transactions.
Flags:
no command-specific flags
This command lists the unique descriptions that appear in transactions, in alphabetic order. You can add a query to select a subset of transactions.
Example:
$ hledger descriptions
Store Name
Gas Station | Petrol
Person A
files
List all files included in the journal. With a REGEX argument, only file names matching the regular expression (case sensitive) are shown.
Flags:
no command-specific flags
notes
List the unique notes that appear in transactions.
Flags:
no command-specific flags
This command lists the unique notes that appear in transactions, in alphabetic order. You can add a query to select a subset of transactions. The note is the part of the transaction description after a | character (or if there is no |, the whole description).
Example:
$ hledger notes
Petrol
Snacks
payees
List the payee/payer names used or declared in the journal.
Flags:
--used list payees used
--declared list payees declared
--undeclared list payees used but not declared
--unused list payees declared but not used
--find list the first payee matched by the first
argument (a case-insensitive infix regexp)
This command lists unique payee/payer names - all of them by default, or
just the ones which have been used in transaction descriptions, or
declared with payee directives, or used but not declared, or declared
but not used, or just the first one matched by a pattern (with --find,
returning a non-zero exit code if it fails).
The payee/payer name is the part of the transaction description before a | character (or if there is no |, the whole description).
You can add query arguments to select a subset of transactions or payees.
Example:
$ hledger payees
Store Name
Gas Station
Person A
prices
Print the market prices declared with P directives. With --infer-market-prices, also show any additional prices inferred from costs. With --show-reverse, also show additional prices inferred by reversing known prices.
Flags:
--show-reverse also show the prices inferred by reversing known
prices
Price amounts are always displayed with their full precision, except for reverse prices which are limited to 8 decimal digits.
Prices can be filtered by a date:, cur: or amt: query.
Generally if you run this command with --infer-market-prices --show-reverse, it will show the same prices used internally to calculate value reports. But if in doubt, you can inspect those directly by running the value report with --debug=2.
stats
Show journal and performance statistics.
Flags:
-1 show a single line of output
-v --verbose show more detailed output
-o --output-file=FILE write output to FILE.
The stats command shows summary information for the whole journal, or a matched part of it. With a reporting interval, it shows a report for each report period.
It also shows some performance statistics:
- how long the program ran for
- the number of transactions processed per second
- the peak live memory in use by the program to do its work
- the peak allocated memory as seen by the program
By default, the output is reasonably discreet; it reveals the main file name, your activity level, and the speed of your machine.
With -v/--verbose, more details are shown: the full paths of all
files, and the names of the commodities you work with.
With -1, only one line of output is shown, in a machine-friendly
tab-separated format: the program version, the main journal file name,
and the performance stats,
The run time of stats is similar to that of a balance report.
Example:
$ hledger stats -f examples/1ktxns-1kaccts.journal
Main file : .../1ktxns-1kaccts.journal
Included files : 0
Txns span : 2000-01-01 to 2002-09-27 (1000 days)
Last txn : 2002-09-26 (7827 days ago)
Txns : 1000 (1.0 per day)
Txns last 30 days : 0 (0.0 per day)
Txns last 7 days : 0 (0.0 per day)
Payees/descriptions : 1000
Accounts : 1000 (depth 10)
Commodities : 26
Market prices : 1000
Runtime stats : 0.12 s elapsed, 8266 txns/s, 4 MB live, 16 MB alloc
$ hledger stats -1 -f examples/10ktxns-1kaccts.journal
1.50.99-g0835a2485-20251119, mac-aarch64 10ktxns-1kaccts.journal 0.66 s elapsed 15244 txns/s 28 MB live 86 MB alloc
This command supports the -o/--output-file option (but not -O/--output-format).
tags
List the tag names used or declared in the journal, or their values.
Flags:
--used list tags used
--declared list tags declared
--undeclared list tags used but not declared
--unused list tags declared but not used
--find list the first tag whose name is matched by the
first argument (a case-insensitive infix regexp)
--values list tag values instead of tag names
--parsed show them in the order they were parsed (mostly),
including duplicates
This command lists tag names - all of them by default, or just the ones
which have been used on transactions/postings/accounts, or declared with
tag directives, or used but not declared, or declared but not used, or
just the first one matched by a pattern (with --find, returning a
non-zero exit code if it fails).
Note this command's non-standard first argument: it is a case-insensitive infix regular expression for matching tag names, which limits the tags shown. Any additional arguments are standard query arguments, which limit the transactions, postings, or accounts providing tags.
With --values, the tags' unique non-empty values are listed instead.
With -E/--empty, blank/empty values are also shown.
With --parsed, tags or values are shown in the order they were parsed,
with duplicates included. (Except, tags from account declarations are
always shown first.)
Remember that accounts also acquire tags from their parents; postings also acquire tags from their account and transaction; and transactions also acquire tags from their postings.
Standard report commands
Show full journal entries, representing transactions.
Flags:
-x --explicit show all amounts explicitly
--invert display all amounts with reversed sign
--locations add tags showing file paths and line numbers
-m --match=DESC fuzzy search for one recent transaction with
description closest to DESC
--new show only newer-dated transactions added in each
file since last run
--round=TYPE how much rounding or padding should be done when
displaying amounts ?
none - show original decimal digits,
as in journal (default)
soft - just add or remove decimal zeros
to match precision
hard - round posting amounts to precision
(can unbalance transactions)
all - also round cost amounts to precision
(can unbalance transactions)
--base-url=URLPREFIX in html output, generate links to hledger-web,
with this prefix. (Usually the base url shown by
hledger-web; can also be relative.)
-O --output-format=FMT select the output format. Supported formats:
txt, beancount, csv, tsv, html, fods, json, sql.
-o --output-file=FILE write output to FILE. A file extension matching
one of the above formats selects that format.
The print command displays full journal entries (transactions) from the
journal file, sorted by date (or with --date2, by secondary
date).
Directives and inter-transaction comments are not shown, currently. This means the print command is somewhat lossy, and if you are using it to reformat/regenerate your journal you should take care to also copy over the directives and inter-transaction comments.
Eg:
$ hledger print -f examples/sample.journal date:200806
2008/06/01 gift
assets:bank:checking $1
income:gifts $-1
2008/06/02 save
assets:bank:saving $1
assets:bank:checking $-1
2008/06/03 * eat & shop
expenses:food $1
expenses:supplies $1
assets:cash $-2
print amount explicitness
Normally, whether posting amounts are implicit or explicit is preserved. For example, when an amount is omitted in the journal, it will not appear in the output. Similarly, if a conversion cost is implied but not written, it will not appear in the output.
You can use the -x/--explicit flag to force explicit display of all
amounts and costs. This can be useful for troubleshooting or for making
your journal more readable and robust against data entry errors. -x is
also implied by using any of -B,-V,-X,--value.
The -x/--explicit flag will cause any postings with a
multi-commodity amount (which can arise when a multi-commodity
transaction has an implicit amount) to be split into multiple
single-commodity postings, keeping the output parseable.
print alignment
Amounts are shown right-aligned within each transaction (but not aligned across all transactions; you can achieve that with ledger-mode in Emacs).
print amount style
Amounts will be displayed mostly in their commodity's display
style, with standardised symbol placement,
decimal mark, and digit group marks. This does not apply to their
decimal digits; print normally shows the same decimal digits that are
recorded in each journal entry.
You can override the decimal precisions with print's special
--round option (since 1.32). --round tries to show amounts with
their commodities' standard decimal precisions, increasingly strongly:
--round=noneshow amounts with original precisions (default)--round=softadd/remove decimal zeros in amounts (except costs)--round=hardround amounts (except costs), possibly hiding significant digits--round=allround all amounts and costs
soft is good for non-lossy cleanup, displaying more consistent
decimals where possible, without making entries unbalanced.
hard or all can be good for stronger cleanup, when decimal rounding
is wanted. Note rounding can produce unbalanced journal entries, perhaps
requiring manual fixup.
print parseability
Normally, print's output is a valid hledger journal, which you can "pipe" to a second hledger command for further processing. This is sometimes convenient for achieving certain kinds of query (though less needed now that queries have become more powerful):
# Show running total of food expenses paid from cash.
# -f- reads from stdin. -I/--ignore-assertions is sometimes needed.
$ hledger print assets:cash | hledger -f- -I reg expenses:food
But here are some things which can cause print's output to become unparseable:
--round(see above) can disrupt transaction balancing.- Account aliases or pivoting can disrupt account names, balance assertions, or balance assignments.
- Value reporting also can disrupt balance assertions or balance assignments.
- Auto postings can generate too many amountless postings.
--infer-costs or --infer-equitycan generate too-complex redundant costs.- Because print always shows transactions in date order, balance assertions involving non-date-ordered transactions (and same-day postings) could be disrupted.
print, other features
With -B/--cost, amounts with
costs are shown converted to
cost.
With --invert, posting amounts are shown with their sign flipped. It
could be useful if you have accidentally recorded some transactions with
the wrong signs.
With --new, print shows only transactions it has not seen on a
previous run. This uses the same deduplication system as the
import command. (See import's docs for details.)
With -m DESC/--match=DESC, print shows one recent transaction whose
description is most similar to DESC. DESC should contain at least two
characters. If there is no similar-enough match, no transaction will be
shown and the program exit code will be non-zero.
With --locations, print adds the source file and line number to every
transaction, as a tag.
print output format
This command also supports the output
destination and output
format options The output formats supported
are txt, beancount (Added in 1.32), csv, tsv (Added in
1.32), json and sql.
The beancount format tries to produce Beancount-compatible output, as
follows:
- Transaction and postings with unmarked status are converted to cleared
(
*) status. - Transactions' payee and note are backslash-escaped and double-quote-escaped and wrapped in double quotes.
- Transaction tags are copied to Beancount #tag format.
- Commodity symbols are converted to upper case, and a small number of
currency symbols like
$are converted to the corresponding currency names. - Account name parts are capitalised and unsupported characters are
replaced with
-. If an account name part does not begin with a letter, or if the first part is not Assets, Liabilities, Equity, Income, or Expenses, an error is raised. (Use--aliasoptions to bring your accounts into compliance.) - An
opendirective is generated for each account used, on the earliest transaction date.
Some limitations:
- Balance assertions are removed.
- Balance assignments become missing amounts.
- Virtual and balanced virtual postings become regular postings.
- Directives are not converted.
Here's an example of print's CSV output:
$ hledger print -Ocsv
"txnidx","date","date2","status","code","description","comment","account","amount","commodity","credit","debit","posting-status","posting-comment"
"1","2008/01/01","","","","income","","assets:bank:checking","1","$","","1","",""
"1","2008/01/01","","","","income","","income:salary","-1","$","1","","",""
"2","2008/06/01","","","","gift","","assets:bank:checking","1","$","","1","",""
"2","2008/06/01","","","","gift","","income:gifts","-1","$","1","","",""
"3","2008/06/02","","","","save","","assets:bank:saving","1","$","","1","",""
"3","2008/06/02","","","","save","","assets:bank:checking","-1","$","1","","",""
"4","2008/06/03","","*","","eat & shop","","expenses:food","1","$","","1","",""
"4","2008/06/03","","*","","eat & shop","","expenses:supplies","1","$","","1","",""
"4","2008/06/03","","*","","eat & shop","","assets:cash","-2","$","2","","",""
"5","2008/12/31","","*","","pay off","","liabilities:debts","1","$","","1","",""
"5","2008/12/31","","*","","pay off","","assets:bank:checking","-1","$","1","","",""
- There is one CSV record per posting, with the parent transaction's fields repeated.
- The "txnidx" (transaction index) field shows which postings belong to the same transaction. (This number might change if transactions are reordered within the file, files are parsed/included in a different order, etc.)
- The amount is separated into "commodity" (the symbol) and "amount" (numeric quantity) fields.
- The numeric amount is repeated in either the "credit" or "debit" column, for convenience. (Those names are not accurate in the accounting sense; it just puts negative amounts under credit and zero or greater amounts under debit.)
aregister
(areg)
Show the transactions and running balances in one account, with each transaction on one line.
Flags:
--txn-dates filter strictly by transaction date, not posting
date. Warning: this can show a wrong running
balance.
--no-elide don't show only 2 commodities per amount
--cumulative accumulation mode: show running total from report
start date
-H --historical accumulation mode: show historical running
total/balance (includes postings before report
start date) (default)
--invert display all amounts with reversed sign
--drop=N omit N leading account name parts
--heading=YN show heading row above table: yes (default) or no
-w --width=N set output width (default: terminal width). -wN,M
sets description width as well.
--align-all guarantee alignment across all lines (slower)
-O --output-format=FMT select the output format. Supported formats:
txt, html, csv, tsv, json.
-o --output-file=FILE write output to FILE. A file extension matching
one of the above formats selects that format.
aregister shows the overall transactions affecting a particular
account (and any subaccounts). Each report line represents one
transaction in this account. Transactions before the report start date
are included in the running balance (--historical mode is the
default). You can suppress this behaviour using the --cumulative
option.
This is a more "real world", bank-like view than the
register command (which shows individual postings,
possibly from multiple accounts, not necessarily in historical mode). As
a quick rule of thumb:
aregisteris best when reconciling real-world asset/liability accountsregisteris best when reviewing individual revenues/expenses.
Note this command's non-standard, and required, first argument; it
specifies the account whose register will be shown. You can write the
account's name, or (to save typing) a case-insensitive infix regular
expression matching the name, which selects the alphabetically first
matched account. (For example, if you have assets:personal checking
and assets:business checking, hledger areg checking would select
assets:business checking.)
Transactions involving subaccounts of this account will also be shown.
aregister ignores depth limits, so its final total will always match a
historical balance report with similar arguments.
Any additional arguments are standard query arguments, which will limit the transactions shown. Note some queries will disturb the running balance, causing it to be different from the account's real-world running balance.
An example: this shows the transactions and historical running balance during july, in the first account whose name contains "checking":
$ hledger areg checking date:jul
Each aregister line item shows:
- the transaction's date (or the relevant posting's date if different, see below)
- the names of all the other account(s) involved in this transaction (probably abbreviated)
- the total change to this account's balance from this transaction
- the account's historical running balance after this transaction.
Transactions making a net change of zero are not shown by default; add
the -E/--empty flag to show them.
For performance reasons, column widths are chosen based on the first
1000 lines; this means unusually wide values in later lines can cause
visual discontinuities as column widths are adjusted. If you want to
ensure perfect alignment, at the cost of more time and memory, use the
--align-all flag.
By default, aregister shows a heading above the data. However, when
reporting in a language different from English, it is easier to omit
this heading and prepend your own one. For this purpose, use the
--heading=no option.
This command also supports the output
destination and output
format options. The output formats
supported are txt, csv, tsv (Added in 1.32), html, fods
(Added in 1.41) and json.
aregister and posting dates
aregister always shows one line (and date and amount) per transaction.
But sometimes transactions have postings with different dates. Also, not
all of a transaction's postings may be within the report period. To
resolve this, aregister shows the earliest of the transaction's date
and posting dates that is in-period, and the sum of the in-period
postings. In other words it will show a combined line item with just the
earliest date, and the running balance will (temporarily, until the
transaction's last posting) be inaccurate. Use register -H if you
need to see the individual postings.
There is also a --txn-dates flag, which filters strictly by
transaction date, ignoring posting dates. This too can cause an
inaccurate running balance.
register
(reg)
Show postings and their running total.
Flags:
--cumulative accumulation mode: show running total from report
start date (default)
-H --historical accumulation mode: show historical running
total/balance (includes postings before report
start date)
-A --average show running average of posting amounts instead
of total (implies --empty)
-m --match=DESC fuzzy search for one recent posting with
description closest to DESC
-r --related show postings' siblings instead
--invert display all amounts with reversed sign
--drop=N omit N leading account name parts
--sort=FIELDS sort by: date, desc, account, amount, absamount,
or a comma-separated combination of these. For a
descending sort, prefix with -. (Default: date)
-w --width=N set output width (default: terminal width). -wN,M
sets description width as well.
--align-all guarantee alignment across all lines (slower)
--base-url=URLPREFIX in html output, generate links to hledger-web,
with this prefix. (Usually the base url shown by
hledger-web; can also be relative.)
-O --output-format=FMT select the output format. Supported formats:
txt, csv, tsv, html, fods, json.
-o --output-file=FILE write output to FILE. A file extension matching
one of the above formats selects that format.
The register command displays matched postings, across all accounts, in
date order, with their running total or running historical balance. (See
also the aregister command, which shows matched
transactions in a specific account.)
register normally shows line per posting, but note that multi-commodity amounts will occupy multiple lines (one line per commodity).
It is typically used with a query selecting a particular account, to see that account's activity:
$ hledger register checking
2008/01/01 income assets:bank:checking $1 $1
2008/06/01 gift assets:bank:checking $1 $2
2008/06/02 save assets:bank:checking $-1 $1
2008/12/31 pay off assets:bank:checking $-1 0
With --date2, it shows and sorts by secondary date instead.
For performance reasons, column widths are chosen based on the first
1000 lines; this means unusually wide values in later lines can cause
visual discontinuities as column widths are adjusted. If you want to
ensure perfect alignment, at the cost of more time and memory, use the
--align-all flag.
The --historical/-H flag adds the balance from any undisplayed prior
postings to the running total. This is useful when you want to see only
recent activity, with a historically accurate running balance:
$ hledger register checking -b 2008/6 --historical
2008/06/01 gift assets:bank:checking $1 $2
2008/06/02 save assets:bank:checking $-1 $1
2008/12/31 pay off assets:bank:checking $-1 0
The --depth option limits the amount of sub-account detail displayed.
The --drop option will trim leading segments from account names.
The --average/-A flag shows the running average posting amount
instead of the running total (so, the final number displayed is the
average for the whole report period). This flag implies --empty (see
below). It is affected by --historical. It works best when showing
just one account and one commodity.
The --related/-r flag shows the other postings in the transactions
of the postings which would normally be shown.
The --invert flag negates all amounts. For example, it can be used on
an income account where amounts are normally displayed as negative
numbers. It's also useful to show postings on the checking account
together with the related account:
The --sort=FIELDS flag sorts by the fields given, which can be any of
account, amount, absamount, date, or desc/description,
optionally separated by commas. For example, --sort account,amount
will group all transactions in each account, sorted by transaction
amount. Each field can be negated by a preceding -, so
--sort -amount will show transactions ordered from smallest amount to
largest amount.
$ hledger register --related --invert assets:checking
With a reporting interval, register shows summary postings, one per interval, aggregating the postings to each account:
$ hledger register --monthly income
2008/01 income:salary $-1 $-1
2008/06 income:gifts $-1 $-2
Periods with no activity, and summary postings with a zero amount, are
not shown by default; use the --empty/-E flag to see them:
$ hledger register --monthly income -E
2008/01 income:salary $-1 $-1
2008/02 0 $-1
2008/03 0 $-1
2008/04 0 $-1
2008/05 0 $-1
2008/06 income:gifts $-1 $-2
2008/07 0 $-2
2008/08 0 $-2
2008/09 0 $-2
2008/10 0 $-2
2008/11 0 $-2
2008/12 0 $-2
Often, you'll want to see just one line per interval. The --depth
option helps with this, causing subaccounts to be aggregated:
$ hledger register --monthly assets --depth 1
2008/01 assets $1 $1
2008/06 assets $-1 0
2008/12 assets $-1 $-1
Note when using report intervals, if you specify start/end dates these will be adjusted outward if necessary to contain a whole number of intervals. This ensures that the first and last intervals are full length and comparable to the others in the report.
If you have a deeply nested account tree some reports might benefit from
trimming leading segments from the account names using --drop.
$ hledger register --monthly income --drop 1
2008/01 salary $-1 $-1
2008/06 gifts $-1 $-2
With -m DESC/--match=DESC, register does a fuzzy search for one
recent posting whose description is most similar to DESC. DESC should
contain at least two characters. If there is no similar-enough match, no
posting will be shown and the program exit code will be non-zero.
Custom register output
register normally uses the full terminal width (or 80 columns if it
can't detect that). You can override this with the --width/-w
option.
The description and account columns normally share the space equally
(about half of (width - 40) each). You can adjust this by adding a
description width as part of --width's argument, comma-separated:
--width W,D . Here's a diagram (won't display correctly in --help):
<--------------------------------- width (W) ---------------------------------->
date (10) description (D) account (W-41-D) amount (12) balance (12)
DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaa AAAAAAAAAAAA AAAAAAAAAAAA
and some examples:
$ hledger reg # use terminal width (or 80 on windows)
$ hledger reg -w 100 # use width 100
$ hledger reg -w 100,40 # set overall width 100, description width 40
This command also supports the output
destination and output
format options The output formats supported
are txt, csv, tsv (Added in 1.32), and json.
balancesheet
(bs)
Show the end balances in asset and liability accounts. Amounts are shown with normal positive sign, as in conventional financial statements.
Flags:
--sum calculation mode: show sum of posting amounts
(default)
--valuechange calculation mode: show total change of value of
period-end historical balances (caused by deposits,
withdrawals, market price fluctuations)
--gain calculation mode: show unrealised capital
gain/loss (historical balance value minus cost
basis)
--count calculation mode: show the count of postings
--change accumulation mode: accumulate amounts from column
start to column end (in multicolumn reports)
--cumulative accumulation mode: accumulate amounts from report
start (specified by e.g. -b/--begin) to column end
-H --historical accumulation mode: accumulate amounts from
journal start to column end (includes postings
before report start date) (default)
-l --flat list/tree mode: show accounts as a flat list
(default). Amounts exclude subaccount amounts,
except where the account is depth-clipped.
-t --tree list/tree mode: show accounts as a tree. Amounts
include subaccount amounts.
--drop=N in list mode, omit N leading account name parts
--declared include non-parent declared accounts (best used
with -E)
-A --average show a row average column (in multicolumn
reports)
-T --row-total show a row total column (in multicolumn reports)
--summary-only display only row summaries (e.g. row total,
average) (in multicolumn reports)
-N --no-total omit the final total row
--no-elide in tree mode, don't squash boring parent accounts
--format=FORMATSTR use this custom line format (in simple reports)
-S --sort-amount sort by amount instead of account code/name
-% --percent express values in percentage of each column's
total
--layout=ARG how to show multi-commodity amounts:
'wide[,WIDTH]': all commodities on one line
'tall' : each commodity on a new line
'bare' : bare numbers, symbols in a column
--base-url=URLPREFIX in html output, generate hyperlinks to
hledger-web, with this prefix. (Usually the base
url shown by hledger-web; can also be relative.)
-O --output-format=FMT select the output format. Supported formats:
txt, html, csv, tsv, json.
-o --output-file=FILE write output to FILE. A file extension matching
one of the above formats selects that format.
This command displays a balance sheet, showing historical ending balances of asset and liability accounts. (To see equity as well, use the balancesheetequity command.)
Accounts declared with the Asset, Cash or Liability type are shown
(see account types).
Or if no such accounts are declared, it shows top-level accounts named
asset or liability (case insensitive, plurals allowed) and their
subaccounts.
Example:
$ hledger balancesheet
Balance Sheet 2008-12-31
|| 2008-12-31
====================++============
Assets ||
--------------------++------------
assets:bank:saving || $1
assets:cash || $-2
--------------------++------------
|| $-1
====================++============
Liabilities ||
--------------------++------------
liabilities:debts || $-1
--------------------++------------
|| $-1
====================++============
Net: || 0
This command is a higher-level variant of the balance
command, and supports many of that command's features, such as
multi-period reports. It is similar to
hledger balance -H assets liabilities, but with smarter account
detection, and liabilities displayed with their sign flipped.
This command also supports the output
destination and output
format options The output formats supported
are txt, csv, tsv (Added in 1.32), html, and json.
balancesheetequity
(bse)
This command displays a balance sheet, showing historical ending balances of asset, liability and equity accounts. Amounts are shown with normal positive sign, as in conventional financial statements.
Flags:
--sum calculation mode: show sum of posting amounts
(default)
--valuechange calculation mode: show total change of value of
period-end historical balances (caused by deposits,
withdrawals, market price fluctuations)
--gain calculation mode: show unrealised capital
gain/loss (historical balance value minus cost
basis)
--count calculation mode: show the count of postings
--change accumulation mode: accumulate amounts from column
start to column end (in multicolumn reports)
--cumulative accumulation mode: accumulate amounts from report
start (specified by e.g. -b/--begin) to column end
-H --historical accumulation mode: accumulate amounts from
journal start to column end (includes postings
before report start date) (default)
-l --flat list/tree mode: show accounts as a flat list
(default). Amounts exclude subaccount amounts,
except where the account is depth-clipped.
-t --tree list/tree mode: show accounts as a tree. Amounts
include subaccount amounts.
--drop=N in list mode, omit N leading account name parts
--declared include non-parent declared accounts (best used
with -E)
-A --average show a row average column (in multicolumn
reports)
-T --row-total show a row total column (in multicolumn reports)
--summary-only display only row summaries (e.g. row total,
average) (in multicolumn reports)
-N --no-total omit the final total row
--no-elide in tree mode, don't squash boring parent accounts
--format=FORMATSTR use this custom line format (in simple reports)
-S --sort-amount sort by amount instead of account code/name
-% --percent express values in percentage of each column's
total
--layout=ARG how to show multi-commodity amounts:
'wide[,WIDTH]': all commodities on one line
'tall' : each commodity on a new line
'bare' : bare numbers, symbols in a column
--base-url=URLPREFIX in html output, generate hyperlinks to
hledger-web, with this prefix. (Usually the base
url shown by hledger-web; can also be relative.)
-O --output-format=FMT select the output format. Supported formats:
txt, html, csv, tsv, json.
-o --output-file=FILE write output to FILE. A file extension matching
one of the above formats selects that format.
This report shows accounts declared with the Asset, Cash,
Liability or Equity type (see account
types). Or if no such
accounts are declared, it shows top-level accounts named asset,
liability or equity (case insensitive, plurals allowed) and their
subaccounts.
Example:
$ hledger balancesheetequity
Balance Sheet With Equity 2008-12-31
|| 2008-12-31
====================++============
Assets ||
--------------------++------------
assets:bank:saving || $1
assets:cash || $-2
--------------------++------------
|| $-1
====================++============
Liabilities ||
--------------------++------------
liabilities:debts || $-1
--------------------++------------
|| $-1
====================++============
Equity ||
--------------------++------------
--------------------++------------
|| 0
====================++============
Net: || 0
This command is a higher-level variant of the balance
command, and supports many of that command's features, such as
multi-period reports. It is similar to
hledger balance -H assets liabilities equity, but with smarter account
detection, and liabilities/equity displayed with their sign flipped.
This report is the easiest way to see if the accounting
equation
(A+L+E = 0) is satisfied (after you have done a
close --retain to merge revenues and expenses with
equity, and perhaps added
--infer-equity to balance
your commodity conversions).
This command also supports the output
destination and output
format options The output formats supported
are txt, csv, tsv, html, and json.
cashflow
(cf)
This command displays a (simple) cashflow statement, showing the inflows and outflows affecting "cash" (ie, liquid, easily convertible) assets. Amounts are shown with normal positive sign, as in conventional financial statements.
Flags:
--sum calculation mode: show sum of posting amounts
(default)
--valuechange calculation mode: show total change of value of
period-end historical balances (caused by deposits,
withdrawals, market price fluctuations)
--gain calculation mode: show unrealised capital
gain/loss (historical balance value minus cost
basis)
--count calculation mode: show the count of postings
--change accumulation mode: accumulate amounts from column
start to column end (in multicolumn reports)
(default)
--cumulative accumulation mode: accumulate amounts from report
start (specified by e.g. -b/--begin) to column end
-H --historical accumulation mode: accumulate amounts from
journal start to column end (includes postings
before report start date)
-l --flat list/tree mode: show accounts as a flat list
(default). Amounts exclude subaccount amounts,
except where the account is depth-clipped.
-t --tree list/tree mode: show accounts as a tree. Amounts
include subaccount amounts.
--drop=N in list mode, omit N leading account name parts
--declared include non-parent declared accounts (best used
with -E)
-A --average show a row average column (in multicolumn
reports)
-T --row-total show a row total column (in multicolumn reports)
--summary-only display only row summaries (e.g. row total,
average) (in multicolumn reports)
-N --no-total omit the final total row
--no-elide in tree mode, don't squash boring parent accounts
--format=FORMATSTR use this custom line format (in simple reports)
-S --sort-amount sort by amount instead of account code/name
-% --percent express values in percentage of each column's
total
--layout=ARG how to show multi-commodity amounts:
'wide[,WIDTH]': all commodities on one line
'tall' : each commodity on a new line
'bare' : bare numbers, symbols in a column
--base-url=URLPREFIX in html output, generate hyperlinks to
hledger-web, with this prefix. (Usually the base
url shown by hledger-web; can also be relative.)
-O --output-format=FMT select the output format. Supported formats:
txt, html, csv, tsv, json.
-o --output-file=FILE write output to FILE. A file extension matching
one of the above formats selects that format.
This report shows accounts declared with the Cash type (see account
types). Or if no such
accounts are declared, it shows accounts
- under a top-level account named
asset(case insensitive, plural allowed) - whose name contains some variation of
cash,bank,checkingorsaving.
More precisely: all accounts matching this case insensitive regular expression:
^assets?(:.+)?:(cash|bank|che(ck|que?)(ing)?|savings?|currentcash)(:|$)
and their subaccounts.
An example cashflow report:
$ hledger cashflow
Cashflow Statement 2008
|| 2008
====================++======
Cash flows ||
--------------------++------
assets:bank:saving || $1
assets:cash || $-2
--------------------++------
|| $-1
This command is a higher-level variant of the balance
command, and supports many of that command's features, such as
multi-period reports. It is similar to
hledger balance assets not:fixed not:investment not:receivable, but
with smarter account detection.
This command also supports the output
destination and output
format options The output formats supported
are txt, csv, tsv (Added in 1.32), html, and json.
incomestatement
(is)
Show revenue inflows and expense outflows during the report period. Amounts are shown with normal positive sign, as in conventional financial statements.
Flags:
--sum calculation mode: show sum of posting amounts
(default)
--valuechange calculation mode: show total change of value of
period-end historical balances (caused by deposits,
withdrawals, market price fluctuations)
--gain calculation mode: show unrealised capital
gain/loss (historical balance value minus cost
basis)
--count calculation mode: show the count of postings
--change accumulation mode: accumulate amounts from column
start to column end (in multicolumn reports)
(default)
--cumulative accumulation mode: accumulate amounts from report
start (specified by e.g. -b/--begin) to column end
-H --historical accumulation mode: accumulate amounts from
journal start to column end (includes postings
before report start date)
-l --flat list/tree mode: show accounts as a flat list
(default). Amounts exclude subaccount amounts,
except where the account is depth-clipped.
-t --tree list/tree mode: show accounts as a tree. Amounts
include subaccount amounts.
--drop=N in list mode, omit N leading account name parts
--declared include non-parent declared accounts (best used
with -E)
-A --average show a row average column (in multicolumn
reports)
-T --row-total show a row total column (in multicolumn reports)
--summary-only display only row summaries (e.g. row total,
average) (in multicolumn reports)
-N --no-total omit the final total row
--no-elide in tree mode, don't squash boring parent accounts
--format=FORMATSTR use this custom line format (in simple reports)
-S --sort-amount sort by amount instead of account code/name
-% --percent express values in percentage of each column's
total
--layout=ARG how to show multi-commodity amounts:
'wide[,WIDTH]': all commodities on one line
'tall' : each commodity on a new line
'bare' : bare numbers, symbols in a column
--base-url=URLPREFIX in html output, generate hyperlinks to
hledger-web, with this prefix. (Usually the base
url shown by hledger-web; can also be relative.)
-O --output-format=FMT select the output format. Supported formats:
txt, html, csv, tsv, json.
-o --output-file=FILE write output to FILE. A file extension matching
one of the above formats selects that format.
This command displays an income statement, showing revenues and expenses during one or more periods.
It shows accounts declared with the Revenue or Expense type (see
account types). Or if
no such accounts are declared, it shows top-level accounts named
revenue or income or expense (case insensitive, plurals allowed)
and their subaccounts.
Example:
$ hledger incomestatement
Income Statement 2008
|| 2008
===================++======
Revenues ||
-------------------++------
income:gifts || $1
income:salary || $1
-------------------++------
|| $2
===================++======
Expenses ||
-------------------++------
expenses:food || $1
expenses:supplies || $1
-------------------++------
|| $2
===================++======
Net: || 0
This command is a higher-level variant of the balance
command, and supports many of that command's features, such as
multi-period reports. It is similar to
hledger balance '(revenues|income)' expenses, but with smarter account
detection, and revenues/income displayed with their sign flipped.
This command also supports the output
destination and output
format options The output formats supported
are txt, csv, tsv (Added in 1.32), html, and json.
Advanced report commands
balance
(bal)
A flexible, general purpose "summing" report that shows accounts with some kind of numeric data. This can be balance changes per period, end balances, budget performance, unrealised capital gains, etc.
Flags:
--sum calculation mode: show sum of posting amounts
(default)
--valuechange calculation mode: show total change of value of
period-end historical balances (caused by deposits,
withdrawals, market price fluctuations)
--gain calculation mode: show unrealised capital
gain/loss (historical balance value minus cost
basis)
--budget[=DESCPAT] calculation mode: show sum of posting amounts
together with budget goals defined by periodic
transactions. With a DESCPAT argument (must be
separated by = not space),
use only periodic transactions with matching
description
(case insensitive substring match).
--count calculation mode: show the count of postings
--change accumulation mode: accumulate amounts from column
start to column end (in multicolumn reports,
default)
--cumulative accumulation mode: accumulate amounts from report
start (specified by e.g. -b/--begin) to column end
-H --historical accumulation mode: accumulate amounts from
journal start to column end (includes postings
before report start date)
-l --flat list/tree mode: show accounts as a flat list
(default). Amounts exclude subaccount amounts,
except where the account is depth-clipped.
-t --tree list/tree mode: show accounts as a tree. Amounts
include subaccount amounts.
--drop=N in list mode, omit N leading account name parts
--declared include non-parent declared accounts (best used
with -E)
-A --average show a row average column (in multicolumn
reports)
-T --row-total show a row total column (in multicolumn reports)
--summary-only display only row summaries (e.g. row total,
average) (in multicolumn reports)
-N --no-total omit the final total row
--no-elide in tree mode, don't squash boring parent accounts
--format=FORMATSTR use this custom line format (in simple reports)
-S --sort-amount sort by amount instead of account code/name (in
flat mode). With multiple columns, sorts by the row
total, or by row average if that is displayed.
-% --percent express values in percentage of each column's
total
-r --related show the other accounts transacted with, instead
--invert display all amounts with reversed sign
--transpose switch rows and columns (use vertical time axis)
--layout=ARG how to lay out multi-commodity amounts and the
overall table:
'wide[,W]': commodities on same line, up to W wide
'tall' : commodities on separate lines
'bare' : commodity symbols in a separate column
'tidy' : each data field in its own column
--base-url=URLPREFIX in html output, generate links to hledger-web,
with this prefix. (Usually the base url shown by
hledger-web; can also be relative.)
-O --output-format=FMT select the output format. Supported formats:
txt, html, csv, tsv, json, fods.
-o --output-file=FILE write output to FILE. A file extension matching
one of the above formats selects that format.
balance is one of hledger's oldest and most versatile commands, for
listing account balances, balance changes, values, value changes and
more, during one time period or many. Generally it shows a table, with
rows representing accounts, and columns representing periods.
Note there are some variants of the balance command with convenient
defaults, which are simpler to use: balancesheet,
balancesheetequity, cashflow and
incomestatement. When you need more control, then
use balance.
balance features
Here's a quick overview of the balance command's features, followed
by more detailed descriptions and examples. Many of these work with the
other balance-like commands as well (bs, cf, is..).
balance can show..
- accounts as a list (
-l) or a tree (-t) - optionally depth-limited (
-[1-9]) - sorted by declaration order and name, or by amount
..and their..
- balance changes (the default)
- or actual and planned balance changes (
--budget) - or value of balance changes (
-V) - or change of balance values (
--valuechange) - or unrealised capital gain/loss (
--gain) - or balance changes from sibling postings (
--related/-r) - or postings count (
--count)
..in..
- one time period (the whole journal period by default)
- or multiple periods (
-D,-W,-M,-Q,-Y,-p INTERVAL)
..either..
- per period (the default)
- or accumulated since report start date
(
--cumulative) - or accumulated since account creation
(
--historical/-H)
..possibly converted to..
- cost (
--value=cost[,COMM]/--cost/-B) - or market value, as of transaction dates
(
--value=then[,COMM]) - or at period ends (
--value=end[,COMM]) - or now (
--value=now) - or at some other date (
--value=YYYY-MM-DD)
..with..
- totals (
-T), averages (-A), percentages (-%), inverted sign (--invert) - rows and columns swapped
(
--transpose) - another field used as account name
(
--pivot) - custom-formatted line items (single-period reports only)
(
--format) - commodities displayed on the same line or multiple lines
(
--layout)
This command supports the output destination and
output format options, with output formats txt,
csv, tsv (Added in 1.32), json, and (multi-period reports only:)
html, fods (Added in 1.40). In txt output in a colour-supporting
terminal, negative amounts are shown in red.
Simple balance report
With no arguments, balance shows a list of all accounts and their
change of balance - ie, the sum of posting amounts, both inflows and
outflows - during the entire period of the journal. ("Simple" here
means just one column of numbers, covering a single period. You can also
have multi-period reports, described later.)
For real-world accounts, these numbers will normally be their end balance at the end of the journal period; more on this below.
Accounts are sorted by declaration order if any, and then alphabetically by account name. For instance (using examples/sample.journal):
$ hledger -f examples/sample.journal bal
$1 assets:bank:saving
$-2 assets:cash
$1 expenses:food
$1 expenses:supplies
$-1 income:gifts
$-1 income:salary
$1 liabilities:debts
--------------------
0
Accounts with a zero balance (and no non-zero subaccounts, in tree
mode - see below) are hidden by default. Use -E/--empty to show them
(revealing assets:bank:checking here):
$ hledger -f examples/sample.journal bal -E
0 assets:bank:checking
$1 assets:bank:saving
$-2 assets:cash
$1 expenses:food
$1 expenses:supplies
$-1 income:gifts
$-1 income:salary
$1 liabilities:debts
--------------------
0
The total of the amounts displayed is shown as the last line, unless
-N/--no-total is used.
Balance report line format
For single-period balance reports displayed in the terminal (only), you
can use --format FMT to customise the format and content of each line.
Eg:
$ hledger -f examples/sample.journal balance --format "%20(account) %12(total)"
assets $-1
bank:saving $1
cash $-2
expenses $2
food $1
supplies $1
income $-2
gifts $-1
salary $-1
liabilities:debts $1
---------------------------------
0
The FMT format string specifies the formatting applied to each account/balance pair. It may contain any suitable text, with data fields interpolated like so:
%[MIN][.MAX](FIELDNAME)
-
MIN pads with spaces to at least this width (optional)
-
MAX truncates at this width (optional)
-
FIELDNAME must be enclosed in parentheses, and can be one of:
depth_spacer- a number of spaces equal to the account's depth, or if MIN is specified, MIN * depth spaces.account- the account's nametotal- the account's balance/posted total, right justified
Also, FMT can begin with an optional prefix to control how multi-commodity amounts are rendered:
%_- render on multiple lines, bottom-aligned (the default)%^- render on multiple lines, top-aligned%,- render on one line, comma-separated
There are some quirks. Eg in one-line mode, %(depth_spacer) has no
effect, instead %(account) has indentation built in. Experimentation
may be needed to get pleasing results.
Some example formats:
%(total)- the account's total%-20.20(account)- the account's name, left justified, padded to 20 characters and clipped at 20 characters%,%-50(account) %25(total)- account name padded to 50 characters, total padded to 20 characters, with multiple commodities rendered on one line%20(total) %2(depth_spacer)%-(account)- the default format for the single-column balance report
Filtered balance report
You can show fewer accounts, a different time period, totals from cleared transactions only, etc. by using query arguments or options to limit the postings being matched. Eg:
$ hledger -f examples/sample.journal bal --cleared assets date:200806
$-2 assets:cash
--------------------
$-2
List or tree mode
By default, or with -l/--flat, accounts are shown as a flat list with
their full names visible, as in the examples above.
With -t/--tree, the account hierarchy is shown, with subaccounts'
"leaf" names indented below their parent:
$ hledger -f examples/sample.journal balance
$-1 assets
$1 bank:saving
$-2 cash
$2 expenses
$1 food
$1 supplies
$-2 income
$-1 gifts
$-1 salary
$1 liabilities:debts
--------------------
0
Notes:
-
"Boring" accounts are combined with their subaccount for more compact output, unless
--no-elideis used. Boring accounts have no balance of their own and just one subaccount (egassets:bankandliabilitiesabove). -
All balances shown are "inclusive", ie including the balances from all subaccounts. Note this means some repetition in the output, which requires explanation when sharing reports with non-plaintextaccounting-users. A tree mode report's final total is the sum of the top-level balances shown, not of all the balances shown.
-
Each group of sibling accounts (ie, under a common parent) is sorted separately.
Depth limiting
With a depth:NUM query, or --depth NUM option, or just -NUM (eg:
-3) balance reports will show accounts only to the specified depth,
hiding the deeper subaccounts. This can be useful for getting an
overview without too much detail.
Account balances at the depth limit always include the balances from any deeper subaccounts (even in list mode). Eg, limiting to depth 1:
$ hledger -f examples/sample.journal balance -1
$-1 assets
$2 expenses
$-2 income
$1 liabilities
--------------------
0
Dropping top-level accounts
You can also hide one or more top-level account name parts, using
--drop NUM. This can be useful for hiding repetitive top-level account
names:
$ hledger -f examples/sample.journal bal expenses --drop 1
$1 food
$1 supplies
--------------------
$2
Showing declared accounts
With --declared, accounts which have been declared with an account
directive will be included in the balance report, even if
they have no transactions. (Since they will have a zero balance, you
will also need -E/--empty to see them.)
More precisely, leaf declared accounts (with no subaccounts) will be included, since those are usually the more useful in reports.
The idea of this is to be able to see a useful "complete" balance report, even when you don't have transactions in all of your declared accounts yet.
Sorting by amount
With -S/--sort-amount, accounts with the largest (most positive)
balances are shown first. Eg: hledger bal expenses -MAS shows your
biggest averaged monthly expenses first. When more than one commodity is
present, they will be sorted by the alphabetically earliest commodity
first, and then by subsequent commodities (if an amount is missing a
commodity, it is treated as 0).
Revenues and liability balances are typically negative, however, so -S
shows these in reverse order. To work around this, you can add
--invert to flip the signs. Or you could use one of the higher-level
balance reports (bs, is..), which flip the sign automatically (eg:
hledger is -MAS).
Percentages
With -%/--percent, balance reports show each account's value
expressed as a percentage of the (column) total.
Note it is not useful to calculate percentages if the amounts in a column have mixed signs. In this case, make a separate report for each sign, eg:
$ hledger bal -% amt:`>0`
$ hledger bal -% amt:`<0`
Similarly, if the amounts in a column have mixed commodities, convert
them to one commodity with -B, -V, -X or --value, or make a
separate report for each commodity:
$ hledger bal -% cur:\\$
$ hledger bal -% cur:€
Multi-period balance report
With a report interval (set by the -D/--daily,
-W/--weekly, -M/--monthly, -Q/--quarterly, -Y/--yearly, or
-p/--period flag), balance shows a tabular report, with columns
representing successive time periods (and a title):
$ hledger -f examples/sample.journal bal --quarterly income expenses -E
Balance changes in 2008:
|| 2008q1 2008q2 2008q3 2008q4
===================++=================================
expenses:food || 0 $1 0 0
expenses:supplies || 0 $1 0 0
income:gifts || 0 $-1 0 0
income:salary || $-1 0 0 0
-------------------++---------------------------------
|| $-1 $1 0 0
Notes:
- The report's start/end dates will be expanded, if necessary, to fully encompass the displayed subperiods (so that the first and last subperiods have the same duration as the others).
- Leading and trailing periods (columns) containing all zeroes are not
shown, unless
-E/--emptyis used. - Accounts (rows) containing all zeroes are not shown, unless
-E/--emptyis used. - Amounts with many commodities are shown in abbreviated form, unless
--no-elideis used. - Average and/or total columns can be added with the
-A/--averageand-T/--row-totalflags. - The
--transposeflag can be used to exchange rows and columns. - The
--pivot FIELDoption causes a different transaction field to be used as "account name". See PIVOTING. - The
--summary-onlyflag (--summaryalso works) hides all but the Total and Average columns (those should be enabled with--row-totaland-A/--average).
Multi-period reports with many periods can be too wide for easy viewing in the terminal. Here are some ways to handle that:
- Hide the totals row with
-N/--no-total - Filter to a single currency with
cur: - Convert to a single currency with
-V [--infer-market-price] - Use a more compact layout like
--layout=bare - Maximize the terminal window
- Reduce the terminal's font size
- View with a pager like less, eg:
hledger bal -D --color=yes | less -RS - Output as CSV and use a CSV viewer like
visidata
(
hledger bal -D -O csv | vd -f csv), Emacs' csv-mode (M-x csv-mode, C-c C-a), or a spreadsheet (hledger bal -D -o a.csv && open a.csv) - Output as HTML and view with a browser:
hledger bal -D -o a.html && open a.html
Balance change, end balance
It's important to be clear on the meaning of the numbers shown in balance reports. Here is some terminology we use:
A balance change is the net amount added to, or removed from, an account during some period.
An end balance is the amount accumulated in an account as of some date (and some time, but hledger doesn't store that; assume end of day in your timezone). It is the sum of previous balance changes.
We call it a historical end balance if it includes all balance changes since the account was created. For a real world account, this means it will match the "historical record", eg the balances reported in your bank statements or bank web UI. (If they are correct!)
In general, balance changes are what you want to see when reviewing revenues and expenses, and historical end balances are what you want to see when reviewing or reconciling asset, liability and equity accounts.
balance shows balance changes by default. To see accurate historical
end balances:
-
Initialise account starting balances with an "opening balances" transaction (a transfer from equity to the account), unless the journal covers the account's full lifetime.
-
Include all of of the account's prior postings in the report, by not specifying a report start date, or by using the
-H/--historicalflag. (-Hcauses report start date to be ignored when summing postings.)
Balance report modes
The balance command is quite flexible; here is the full detail on how to control what it reports. If the following seems complicated, don't worry - this is for advanced reporting, and it does take time and experimentation to get familiar with all the report modes.
There are three important option groups:
hledger balance [CALCULATIONMODE] [ACCUMULATIONMODE] [VALUATIONMODE] ...
Calculation mode
The basic calculation to perform for each table cell. It is one of:
--sum: sum the posting amounts (default)--budget: sum the amounts, but also show the budget goal amount (for each account/period)--valuechange: show the change in period-end historical balance values (caused by deposits, withdrawals, and/or market price fluctuations)--gain: show the unrealised capital gain/loss, (the current valued balance minus each amount's original cost)--count: show the count of postings
Accumulation mode
How amounts should accumulate across a report's subperiods/columns. Another way to say it: which time period's postings should contribute to each cell's calculation. It is one of:
-
--change: calculate with postings from column start to column end, ie "just this column". Typically used to see revenues/expenses. (default for balance, cashflow, incomestatement) -
--cumulative: calculate with postings from report start to column end, ie "previous columns plus this column". Typically used to show changes accumulated since the report's start date. Not often used. -
--historical/-H: calculate with postings from journal start to column end, ie "all postings from before report start date until this column's end". Typically used to see historical end balances of assets/liabilities/equity. (default for balancesheet, balancesheetequity)
Valuation mode
Which kind of value or cost conversion should be applied, if any, before displaying the report. See Cost reporting and Value reporting for more about conversions.
A valuation (or cost) mode can be selected with the --value option:
- no conversion : don't convert to cost or value (default)
--value=cost[,COMM]: convert amounts to cost (then optionally to some other commodity)--value=then[,COMM]: convert amounts to market value on transaction dates--value=end[,COMM]: convert amounts to market value on period end date(s)
(default with--valuechange,--gain)--value=now[,COMM]: convert amounts to market value on today's date--value=YYYY-MM-DD[,COMM]: convert amounts to market value on another date
or with the legacy -B/-V/-X options, which are equivalent and easier to type:
-B/--cost: like --value=cost-V/--market: like --value=end-X COMM/--exchange COMM: like --value=end,COMM
Note that --value can also convert to cost, as a convenience; but actually --cost and --value are independent options, and could be used together.
Combining balance report modes
Most combinations of these modes should produce reasonable reports, but if you find any that seem wrong or misleading, let us know. The following restrictions are applied:
--valuechangeimplies--value=end--valuechangemakes--changethe default when used with thebalancesheet/balancesheetequitycommands--cumulativeor--historicaldisables--row-total/-T
For reference, here is what the combinations of accumulation and valuation show:
| Valuation:> Accumulation:v | no valuation | --value= then | --value= end | --value= YYYY-MM-DD /now |
|---|---|---|---|---|
--change | change in period | sum of posting-date market values in period | period-end value of change in period | DATE-value of change in period |
--cumulative | change from report start to period end | sum of posting-date market values from report start to period end | period-end value of change from report start to period end | DATE-value of change from report start to period end |
--historical /-H | change from journal start to period end (historical end balance) | sum of posting-date market values from journal start to period end | period-end value of change from journal start to period end | DATE-value of change from journal start to period end |
Budget report
The --budget report is like a regular balance report, but with two
main differences:
- Budget goals and performance percentages are also shown, in brackets
- Accounts which don't have budget goals are hidden by default.
This is useful for comparing planned and actual income, expenses, time usage, etc.
Periodic transaction rules are used to define budget goals. For example, here's a periodic rule defining monthly goals for bus travel and food expenses:
;; Budget
~ monthly
(expenses:bus) $30
(expenses:food) $400
After recording some actual expenses,
;; Two months worth of expenses
2017-11-01
income $-1950
expenses:bus $35
expenses:food:groceries $310
expenses:food:dining $42
expenses:movies $38
assets:bank:checking
2017-12-01
income $-2100
expenses:bus $53
expenses:food:groceries $380
expenses:food:dining $32
expenses:gifts $100
assets:bank:checking
we can see a budget report like this:
$ hledger bal -M --budget
Budget performance in 2017-11-01..2017-12-31:
|| Nov Dec
===============++============================================
<unbudgeted> || $-425 $-565
expenses || $425 [ 99% of $430] $565 [131% of $430]
expenses:bus || $35 [117% of $30] $53 [177% of $30]
expenses:food || $352 [ 88% of $400] $412 [103% of $400]
---------------++--------------------------------------------
|| 0 [ 0% of $430] 0 [ 0% of $430]
This is "goal-based budgeting"; you define goals for accounts and periods, often recurring, and hledger shows performance relative to the goals. This contrasts with "envelope budgeting", which is more detailed and strict - useful when cash is tight, but also quite a bit more work. https://plaintextaccounting.org/Budgeting has more on this topic.
Using the budget report
Historically this report has been confusing and fragile. hledger's version should be relatively robust and intuitive, but you may still find surprises. Here are more notes to help with learning and troubleshooting.
-
In the above example,
expenses:busandexpenses:foodare shown because they have budget goals during the report period. -
Their parent
expensesis also shown, with budget goals aggregated from the children. -
The subaccounts
expenses:food:groceriesandexpenses:food:diningare not shown since they have no budget goal of their own, but they contribute toexpenses:food's actual amount. -
Unbudgeted accounts
expenses:moviesandexpenses:giftsare also not shown, but they contribute toexpenses's actual amount. -
The other unbudgeted accounts
incomeandassets:bank:checkingare grouped as<unbudgeted>. -
--depthordepth:can be used to limit report depth in the usual way (but will not reveal unbudgeted subaccounts). -
Amounts are always inclusive of subaccounts (even in
-l/--listmode). -
Numbers displayed in a --budget report will not always agree with the totals, because of hidden unbudgeted accounts; this is normal.
-E/--emptycan be used to reveal the hidden accounts. -
In the periodic rules used for setting budget goals, unbalanced postings are convenient.
-
You can filter budget reports with the usual queries, eg to focus on particular accounts. It's common to restrict them to just expenses. (The
<unbudgeted>account is occasionally hard to exclude; this is because of date surprises, discussed below.) -
When you have multiple currencies, you may want to convert them to one (
-X COMM --infer-market-prices) and/or show just one at a time (cur:COMM). If you do need to show multiple currencies at once,--layout barecan be helpful. -
You can "roll over" amounts (actual and budgeted) to the next period with
--cumulative.
See also: https://hledger.org/budgeting.html.
Budget date surprises
With small data, or when starting out, some of the generated budget
goal transaction dates might fall
outside the report periods. Eg with
the following journal and report, the first period appears to have no
expenses:food budget. (Also the <unbudgeted> account should be
excluded by the expenses query, but isn't.):
~ monthly in 2020
(expenses:food) $500
2020-01-15
expenses:food $400
assets:checking
$ hledger bal --budget expenses
Budget performance in 2020-01-15:
|| 2020-01-15
===============++====================
<unbudgeted> || $400
expenses:food || 0 [ 0% of $500]
---------------++--------------------
|| $400 [80% of $500]
In this case, the budget goal transactions are generated on first days
of of month (this can be seen with
hledger print --forecast tag:generated expenses). Whereas the report
period defaults to just the 15th day of january (this can be seen from
the report table's column headings).
To fix this kind of thing, be more explicit about the report period
(and/or the periodic rules' dates). In this case, adding -b 2020 does
the trick.
Selecting budget goals
By default, the budget report uses all available periodic transaction rules to generate goals. This includes rules with a different report interval from your report. Eg if you have daily, weekly and monthly periodic rules, all of these will contribute to the goals in a monthly budget report.
You can select a subset of periodic rules by providing an argument to
the --budget flag. --budget=DESCPAT will match all periodic rules
whose description contains DESCPAT, a case-insensitive substring (not a
regular expression or query). This means you can give your periodic
rules descriptions (remember that two spaces are
needed between
period expression and description), and then select from multiple
budgets defined in your journal.
Budgeting vs forecasting
--forecast and --budget both use the periodic transaction rules in
the journal to generate temporary transactions for reporting purposes.
However they are separate features - though you can use both at the same
time if you want. Here are some differences between them:
| --forecast | --budget |
|---|---|
| is a general option; it enables forecasting with all reports | is a balance command option; it selects the balance report's budget mode |
| generates visible transactions which appear in reports | generates invisible transactions which produce goal amounts |
generates forecast transactions from after the last regular transaction, to the end of the report period; or with an argument --forecast=PERIODEXPR generates them throughout the specified period, both optionally restricted by periods specified in the periodic transaction rules | generates budget goal transactions throughout the report period, optionally restricted by periods specified in the periodic transaction rules |
| uses all periodic rules | uses all periodic rules; or with an argument --budget=DESCPAT uses just the rules matched by DESCPAT |
Balance report layout
The --layout option affects how balance and the other balance-like
commands show multi-commodity amounts and commodity symbols. It can
improve readability, for humans and/or machines (other software). It has
four possible values:
--layout=wide[,WIDTH]: commodities are shown on a single line, optionally elided to WIDTH--layout=tall: each commodity is shown on a separate line--layout=bare: commodity symbols are in their own column, amounts are bare numbers--layout=tidy: data is normalised to easily-consumed "tidy" form, with one row per data value. (This one is currently supported only by thebalancecommand.)
Here are the --layout modes supported by each output
format Only CSV output supports all of them:
| - | txt | csv | html | json | sql |
|---|---|---|---|---|---|
| wide | Y | Y | Y | ||
| tall | Y | Y | Y | ||
| bare | Y | Y | Y | ||
| tidy | Y |
Examples:
Wide layout
With many commodities, reports can be very wide:
$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=wide
Balance changes in 2012-01-01..2014-12-31:
|| 2012 2013 2014 Total
==================++====================================================================================================================================================================================================================
Assets:US:ETrade || 10.00 ITOT, 337.18 USD, 12.00 VEA, 106.00 VHT 70.00 GLD, 18.00 ITOT, -98.12 USD, 10.00 VEA, 18.00 VHT -11.00 ITOT, 4881.44 USD, 14.00 VEA, 170.00 VHT 70.00 GLD, 17.00 ITOT, 5120.50 USD, 36.00 VEA, 294.00 VHT
------------------++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|| 10.00 ITOT, 337.18 USD, 12.00 VEA, 106.00 VHT 70.00 GLD, 18.00 ITOT, -98.12 USD, 10.00 VEA, 18.00 VHT -11.00 ITOT, 4881.44 USD, 14.00 VEA, 170.00 VHT 70.00 GLD, 17.00 ITOT, 5120.50 USD, 36.00 VEA, 294.00 VHT
A width limit reduces the width, but some commodities will be hidden:
$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=wide,32
Balance changes in 2012-01-01..2014-12-31:
|| 2012 2013 2014 Total
==================++===========================================================================================================================
Assets:US:ETrade || 10.00 ITOT, 337.18 USD, 2 more.. 70.00 GLD, 18.00 ITOT, 3 more.. -11.00 ITOT, 3 more.. 70.00 GLD, 17.00 ITOT, 3 more..
------------------++---------------------------------------------------------------------------------------------------------------------------
|| 10.00 ITOT, 337.18 USD, 2 more.. 70.00 GLD, 18.00 ITOT, 3 more.. -11.00 ITOT, 3 more.. 70.00 GLD, 17.00 ITOT, 3 more..
Tall layout
Each commodity gets a new line (may be different in each column), and account names are repeated:
$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=tall
Balance changes in 2012-01-01..2014-12-31:
|| 2012 2013 2014 Total
==================++==================================================
Assets:US:ETrade || 10.00 ITOT 70.00 GLD -11.00 ITOT 70.00 GLD
Assets:US:ETrade || 337.18 USD 18.00 ITOT 4881.44 USD 17.00 ITOT
Assets:US:ETrade || 12.00 VEA -98.12 USD 14.00 VEA 5120.50 USD
Assets:US:ETrade || 106.00 VHT 10.00 VEA 170.00 VHT 36.00 VEA
Assets:US:ETrade || 18.00 VHT 294.00 VHT
------------------++--------------------------------------------------
|| 10.00 ITOT 70.00 GLD -11.00 ITOT 70.00 GLD
|| 337.18 USD 18.00 ITOT 4881.44 USD 17.00 ITOT
|| 12.00 VEA -98.12 USD 14.00 VEA 5120.50 USD
|| 106.00 VHT 10.00 VEA 170.00 VHT 36.00 VEA
|| 18.00 VHT 294.00 VHT
Bare layout
Commodity symbols are kept in one column, each commodity has its own row, amounts are bare numbers, account names are repeated:
$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=bare
Balance changes in 2012-01-01..2014-12-31:
|| Commodity 2012 2013 2014 Total
==================++=============================================
Assets:US:ETrade || GLD 0 70.00 0 70.00
Assets:US:ETrade || ITOT 10.00 18.00 -11.00 17.00
Assets:US:ETrade || USD 337.18 -98.12 4881.44 5120.50
Assets:US:ETrade || VEA 12.00 10.00 14.00 36.00
Assets:US:ETrade || VHT 106.00 18.00 170.00 294.00
------------------++---------------------------------------------
|| GLD 0 70.00 0 70.00
|| ITOT 10.00 18.00 -11.00 17.00
|| USD 337.18 -98.12 4881.44 5120.50
|| VEA 12.00 10.00 14.00 36.00
|| VHT 106.00 18.00 170.00 294.00
Bare layout also affects CSV output, which is useful for producing data that is easier to consume, eg for making charts:
$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -O csv --layout=bare
"account","commodity","balance"
"Assets:US:ETrade","GLD","70.00"
"Assets:US:ETrade","ITOT","17.00"
"Assets:US:ETrade","USD","5120.50"
"Assets:US:ETrade","VEA","36.00"
"Assets:US:ETrade","VHT","294.00"
"Total:","GLD","70.00"
"Total:","ITOT","17.00"
"Total:","USD","5120.50"
"Total:","VEA","36.00"
"Total:","VHT","294.00"
Bare layout will sometimes display an extra row for the no-symbol
commodity, because of zero amounts (hledger treats zeroes as
commodity-less, usually). This can break hledger-bar confusingly
(workaround: add a cur: query to exclude the no-symbol row).
Tidy layout
This produces normalised "tidy data" (see https://cran.r-project.org/web/packages/tidyr/vignettes/tidy-data.html) where every variable has its own column and each row represents a single data point. This is the easiest kind of data for other software to consume:
$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -Y -O csv --layout=tidy
"account","period","start_date","end_date","commodity","value"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","GLD","0"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","ITOT","10.00"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","USD","337.18"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","VEA","12.00"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","VHT","106.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","GLD","70.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","ITOT","18.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","USD","-98.12"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","VEA","10.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","VHT","18.00"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","GLD","0"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","ITOT","-11.00"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","USD","4881.44"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","VEA","14.00"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","VHT","170.00"
Balance report output
As noted in Output format, if you choose HTML output
(by using -O html or -o somefile.html), you can create a
hledger.css file in the same directory to customise the report's
appearance.
The HTML and FODS output
formats can generate hyperlinks to a hledger-web register view for
each account and period. E.g. if your hledger-web server is reachable
at http://localhost:5000 then you might run the balance command with
the extra option --base-url=http://localhost:5000. You can also
produce relative links, like --base-url="some/path" or
--base-url="".)
Some useful balance reports
Some frequently used balance options/reports are:
-
bal -M revenues expenses
Show revenues/expenses in each month. Also available as theincomestatementcommand. -
bal -M -H assets liabilities
Show historical asset/liability balances at each month end. Also available as thebalancesheetcommand. -
bal -M -H assets liabilities equity
Show historical asset/liability/equity balances at each month end. Also available as thebalancesheetequitycommand. -
bal -M assets not:receivable
Show changes to liquid assets in each month. Also available as thecashflowcommand.
Also:
-
bal -M expenses -2 -SA
Show monthly expenses summarised to depth 2 and sorted by average amount. -
bal -M --budget expenses
Show monthly expenses and budget goals. -
bal -M --valuechange investments
Show monthly change in market value of investment assets. -
bal investments --valuechange -D date:lastweek amt:'>1000' -STA [--invert]
Show top gainers [or losers] last week
roi
Shows the time-weighted (TWR) and money-weighted (IRR) rate of return on your investments.
Flags:
--cashflow show all amounts that were used to compute
returns
--investment=QUERY query to select your investment transactions
--profit-loss=QUERY --pnl query to select profit-and-loss or
appreciation/valuation transactions
At a minimum, you need to supply a query (which could be just an account
name) to select your investment(s) with --inv, and another query to
identify your profit and loss transactions with --pnl.
If you do not record changes in the value of your investment manually,
or do not require computation of time-weighted return (TWR), --pnl
could be an empty query (--pnl "" or --pnl STR where STR does not
match any of your accounts).
This command will compute and display the internalized rate of return (IRR, also known as money-weighted rate of return) and time-weighted rate of return (TWR) for your investments for the time period requested. IRR is always annualized due to the way it is computed, but TWR is reported both as a rate over the chosen reporting period and as an annual rate.
Price directives will be taken into account if you supply appropriate
--cost or --value flags (see
VALUATION).
Note, in some cases this report can fail, for these reasons:
- Error (NotBracketed): No solution for Internal Rate of Return (IRR). Possible causes: IRR is huge (>1000000%), balance of investment becomes negative at some point in time.
- Error (SearchFailed): Failed to find solution for Internal Rate of Return (IRR). Either search does not converge to a solution, or converges too slowly.
Examples:
-
Using roi to compute total return of investment in stocks: https://github.com/simonmichael/hledger/blob/master/examples/investing/roi-unrealised.ledger
-
Cookbook > Return on Investment: https://hledger.org/roi.html
Spaces and special characters in --inv and --pnl
Note that --inv and --pnl's argument is a query, and queries could
have several space-separated terms (see
QUERIES).
To indicate that all search terms form single command-line argument, you will need to put them in quotes (see Special characters):
$ hledger roi --inv 'term1 term2 term3 ...'
If any query terms contain spaces themselves, you will need an extra level of nested quoting, eg:
$ hledger roi --inv="'Assets:Test 1'" --pnl="'Equity:Unrealized Profit and Loss'"
Semantics of --inv and --pnl
Query supplied to --inv has to match all transactions that are related
to your investment. Transactions not matching --inv will be ignored.
In these transactions, ROI will conside postings that match --inv to
be "investment postings" and other postings (not matching --inv)
will be sorted into two categories: "cash flow" and "profit and
loss", as ROI needs to know which part of the investment value is your
contributions and which is due to the return on investment.
-
"Cash flow" is depositing or withdrawing money, buying or selling assets, or otherwise converting between your investment commodity and any other commodity. Example:
2019-01-01 Investing in Snake Oil assets:cash -$100 investment:snake oil 2020-01-01 Selling my Snake Oil assets:cash $10 investment:snake oil = 0 -
"Profit and loss" is change in the value of your investment:
2019-06-01 Snake Oil falls in value investment:snake oil = $57 equity:unrealized profit or loss
All non-investment postings are assumed to be "cash flow", unless they
match --pnl query. Changes in value of your investment due to "profit
and loss" postings will be considered as part of your investment
return.
Example: if you use --inv snake --pnl equity:unrealized, then postings
in the example below would be classifed as:
2019-01-01 Snake Oil #1
assets:cash -$100 ; cash flow posting
investment:snake oil ; investment posting
2019-03-01 Snake Oil #2
equity:unrealized pnl -$100 ; profit and loss posting
snake oil ; investment posting
2019-07-01 Snake Oil #3
equity:unrealized pnl ; profit and loss posting
cash -$100 ; cash flow posting
snake oil $50 ; investment posting
IRR and TWR explained
"ROI" stands for "return on investment". Traditionally this was computed as a difference between current value of investment and its initial value, expressed in percentage of the initial value.
However, this approach is only practical in simple cases, where investments receives no in-flows or out-flows of money, and where rate of growth is fixed over time. For more complex scenarios you need different ways to compute rate of return, and this command implements two of them: IRR and TWR.
Internal rate of return, or "IRR" (also called "money-weighted rate of return") takes into account effects of in-flows and out-flows, and the time between them. Investment at a particular fixed interest rate is going to give you more interest than the same amount invested at the same interest rate, but made later in time. If you are withdrawing from your investment, your future gains would be smaller (in absolute numbers), and will be a smaller percentage of your initial investment, so your IRR will be smaller. And if you are adding to your investment, you will receive bigger absolute gains, which will be a bigger percentage of your initial investment, so your IRR will be larger.
As mentioned before, in-flows and out-flows would be any cash that you
personally put in or withdraw, and for the "roi" command, these are
the postings that match the query in the--inv argument and NOT match
the query in the--pnl argument.
If you manually record changes in the value of your investment as transactions that balance them against "profit and loss" (or "unrealized gains") account or use price directives, then in order for IRR to compute the precise effect of your in-flows and out-flows on the rate of return, you will need to record the value of your investement on or close to the days when in- or out-flows occur.
In technical terms, IRR uses the same approach as computation of net
present value, and tries to find a discount rate that makes net present
value of all the cash flows of your investment to add up to zero. This
could be hard to wrap your head around, especially if you haven't done
discounted cash flow analysis before. Implementation of IRR in hledger
should produce results that match the =XIRR formula in Excel.
Second way to compute rate of return that roi command implements is
called "time-weighted rate of return" or "TWR". Like IRR, it will
account for the effect of your in-flows and out-flows, but unlike IRR it
will try to compute the true rate of return of the underlying asset,
compensating for the effect that deposits and withdrawas have on the
apparent rate of growth of your investment.
TWR represents your investment as an imaginary "unit fund" where in-flows/ out-flows lead to buying or selling "units" of your investment and changes in its value change the value of "investment unit". Change in "unit price" over the reporting period gives you rate of return of your investment, and make TWR less sensitive than IRR to the effects of cash in-flows and out-flows.
References:
- Explanation of rate of return
- Explanation of IRR
- Explanation of TWR
- IRR vs TWR
- Examples of computing IRR and TWR and discussion of the limitations of both metrics
Chart commands
activity
Show an ascii barchart of posting counts per interval.
Flags:
no command-specific flags
The activity command displays an ascii histogram showing transaction counts by day, week, month or other reporting interval (by day is the default). With query arguments, it counts only matched transactions.
Examples:
$ hledger activity --quarterly
2008-01-01 **
2008-04-01 *******
2008-07-01
2008-10-01 **
Data generation commands
close
(equity)
close prints several kinds of "closing" and/or "opening"
transactions, useful in various situations: migrating balances to a new
journal file, retaining earnings into equity, consolidating balances,
viewing lot costs.. Like print, it prints valid journal entries. You
can copy these into your journal file(s) when you are happy with how
they look.
Flags:
--clopen[=TAGVAL] show closing and opening balances transactions,
for AL accounts by default
--close[=TAGVAL] show just a closing balances transaction
--open[=TAGVAL] show just an opening balances transaction
--assert[=TAGVAL] show a balance assertions transaction
--assign[=TAGVAL] show a balance assignments transaction
--retain[=TAGVAL] show a retain earnings transaction, for RX
accounts by default
-x --explicit show all amounts explicitly
--show-costs show amounts with different costs separately
--interleaved show source and destination postings together
--assertion-type=TYPE =, ==, =* or ==*
--close-desc=DESC set closing transaction's description
--close-acct=ACCT set closing transaction's destination account
--open-desc=DESC set opening transaction's description
--open-acct=ACCT set opening transaction's source account
--round=TYPE how much rounding or padding should be done when
displaying amounts ?
none - show original decimal digits,
as in journal (default)
soft - just add or remove decimal zeros
to match precision
hard - round posting amounts to precision
(can unbalance transactions)
all - also round cost amounts to precision
(can unbalance transactions)
close has six modes, selected by choosing one of the mode flags:
--clopen, --close (default), --open, --assert, --assign, or
--retain. They are all doing the same kind of operation, but with
different defaults for different situations.
The journal entries generated by close will have a clopen: tag,
which is helpful when you want to exclude them from reports. If the main
journal file name contains a number, the tag's value will be that base
file name with the number incremented. Eg if the journal file is
2025.journal, the tag will be clopen:2026. Or you can set the tag
value by providing an argument to the mode flag. Eg --close=foo or
--clopen=2025-main.
close --clopen
This is useful if migrating balances to a new journal file at the start of a new year. It prints a "closing balances" transaction that zeroes out account balances (Asset and Liability accounts, by default), and an opposite "opening balances" transaction that restores them again. Typically, you would run
hledger close --clopen -e NEWYEAR >> $LEDGER_FILE
and then move the opening transaction from the old file to the new file (and probably also update your LEDGER_FILE environment variable).
Why might you do this ? If your reports are fast, you may not need it. But at some point you will probably want to partition your data by time, for performance or data integrity or regulatory reasons. A new file or set of files per year is common. Then, having each file/fileset "bookended" with opening and closing balance transactions will allow you to freely pick and choose which files to read - just the current year, any past year, any sequence of years, or all of them - while showing correct account balances in each case. The earliest opening balances transaction sets correct starting balances, and any later closing/opening pairs will harmlessly cancel each other out.
The balances will be transferred to and from
equity:opening/closing balances by default. You can override this by
using --close-acct and/or --open-acct.
You can select a different set of accounts to close/open by providing an
account query. Eg to add Equity accounts, provide arguments like
assets liabilities equity or type:ALE. When migrating to a new file,
you'll usually want to bring along the AL or ALE accounts, but not the
RX accounts (Revenue, Expense).
Assertions will be added indicating and checking the new balances of the closed/opened accounts.
close --close
This prints just the closing balances transaction of --clopen. It is
the default if you don't specify a mode.
More customisation options are described below. Among other things, you
can use close --close to generate a transaction moving the balances
from any set of accounts, to a different account. (If you need to move
just a portion of the balance, see
hledger-move.)
close --open
This prints just the opening balances transaction of --clopen. (It is
similar to Ledger's equity
command.)
close --assert
This prints a transaction that asserts the
account balances as they are on the end date (and adds an assert:
tag). It could be useful as documention and to guard against changes.
close --assign
This prints a transaction that assigns the account balances as they are on the end date (and adds an "assign:" tag). Unlike balance assertions, assignments will post changes to balances as needed to reach the specified amounts.
This is another way to set starting balances when migrating to a new
file, and it will set them correctly even in the presence of earlier
files which do not have a closing balances transaction. However, it can
hide errors, and disturb the accounting equation, so --clopen is
usually
recommended.
close --retain
This is like --close, but it closes Revenue and Expense account
balances by default. They will be transferred to
equity:retained earnings, or another account specified with
--close-acct.
Revenues and expenses correspond to changes in equity. They are categorised separately for reporting purposes, but traditionally at the end of each accounting period, businesses consolidate them into equity, This is called "retaining earnings", or "closing the books".
In personal accounting, there's not much reason to do this, and most
people don't. (One reason to do it is to help the balancesheetequity
report show a zero total, demonstrating that the accounting equation
(A-L=E) is satisfied.)
close customisation
In all modes, the following things can be overridden:
- the accounts to be closed/opened, with account query arguments
- the closing/opening dates, with
-e OPENDATE - the balancing account, with
--close-acct=ACCTand/or--open-acct=ACCT - the transaction descriptions, with
--close-desc=DESCand--open-desc=DESC - the transactions'
clopentag value, with aTAGVALargument for the mode flag (see above).
By default, the closing date is yesterday, or the journal's end date,
whichever is later; and the opening date is always one day after the
closing date. You can change these by specifying a report end
date; the closing date will be the last day of
the report period. Eg -e 2024 means "close on 2023-12-31, open on
2024-01-01".
With --x/--explicit, the balancing amount will be shown explicitly,
and if it involves multiple commodities, a separate posting will be
generated for each of them (similar to print -x).
With --interleaved, each individual transfer is shown with source and
destination postings next to each other (perhaps useful for
troubleshooting).
With --show-costs, balances' costs are also shown, with different
costs kept separate. This may generate very large journal entries, if
you have many currency conversions or investment transactions.
close --show-costs is currently the best way to view investment lots
with hledger. (To move or dispose of lots, see the more capable
hledger-move script.)
close and balance assertions
close adds balance assertions verifying that the accounts have been
reset to zero in a closing transaction or restored to their previous
balances in an opening transaction. These provide useful error checking,
but you can ignore them temporarily with -I, or remove them if you
prefer.
Single-commodity, subaccount-exclusive balance assertions (=) are
generated by default. This can be changed with --assertion-type='==*'
(eg).
When running close you should probably avoid using -C, -R,
status: (filtering by status or realness) or --auto (generating
postings), since the generated balance assertions would then require
these.
Transactions with multiple dates (eg posting dates) spanning the file boundary also can disrupt the balance assertions:
2023-12-30 a purchase made in december, cleared in january
expenses:food 5
assets:bank:checking -5 ; date: 2023-01-02
To solve this you can transfer the money to and from a temporary account, splitting the multi-day transaction into two single-day transactions:
; in 2022.journal:
2022-12-30 a purchase made in december, cleared in january
expenses:food 5
equity:pending -5
; in 2023.journal:
2023-01-02 last year's transaction cleared
equity:pending 5 = 0
assets:bank:checking -5
close examples
Retain earnings
Record 2022's revenues/expenses as retained earnings on 2022-12-31, appending the generated transaction to the journal:
$ hledger close --retain -f 2022.journal -p 2022 >> 2022.journal
After this, to see 2022's revenues and expenses you must exclude the retain earnings transaction:
$ hledger -f 2022.journal is not:desc:'retain earnings'
Migrate balances to a new file
Close assets/liabilities on 2022-12-31 and re-open them on 2023-01-01:
$ hledger close --clopen -f 2022.journal -p 2022
# copy/paste the closing transaction to the end of 2022.journal
# copy/paste the opening transaction to the start of 2023.journal
After this, to see 2022's end-of-year balances you must exclude the closing balances transaction:
$ hledger -f 2022.journal bs not:desc:'closing balances'
For more flexibility, it helps to tag closing and opening transactions
with eg clopen:NEWYEAR, then you can ensure correct balances by
excluding all opening/closing transactions except the first, like so:
$ hledger bs -Y -f 2021.j -f 2022.j -f 2023.j expr:'tag:clopen=2021 or not tag:clopen'
$ hledger bs -Y -f 2021.j -f 2022.j expr:'tag:clopen=2021 or not tag:clopen'
$ hledger bs -Y -f 2022.j -f 2023.j expr:'tag:clopen=2022 or not tag:clopen'
$ hledger bs -Y -f 2021.j expr:'tag:clopen=2021 or not tag:clopen'
$ hledger bs -Y -f 2022.j expr:'tag:clopen=2022 or not tag:clopen'
$ hledger bs -Y -f 2023.j # unclosed file, no query needed
More detailed close examples
See examples/multi-year.
rewrite
Print all transactions, rewriting the postings of matched transactions. For now the only rewrite available is adding new postings, like print --auto.
Flags:
--add-posting='ACCT AMTEXPR' add a posting to ACCT, which may be
parenthesised. AMTEXPR is either a literal
amount, or *N which means the transaction's
first matched amount multiplied by N (a
decimal number). Two spaces separate ACCT
and AMTEXPR.
--diff generate diff suitable as an input for
patch tool
This is a start at a generic rewriter of transaction entries. It reads the default journal and prints the transactions, like print, but adds one or more specified postings to any transactions matching QUERY. The posting amounts can be fixed, or a multiplier of the existing transaction's first posting amount.
Examples:
$ hledger-rewrite.hs ^income --add-posting '(liabilities:tax) *.33 ; income tax' --add-posting '(reserve:gifts) $100'
$ hledger-rewrite.hs expenses:gifts --add-posting '(reserve:gifts) *-1"'
$ hledger-rewrite.hs -f rewrites.hledger
rewrites.hledger may consist of entries like:
= ^income amt:<0 date:2017
(liabilities:tax) *0.33 ; tax on income
(reserve:grocery) *0.25 ; reserve 25% for grocery
(reserve:) *0.25 ; reserve 25% for grocery
Note the single quotes to protect the dollar sign from bash, and the two spaces between account and amount.
More:
$ hledger rewrite [QUERY] --add-posting "ACCT AMTEXPR" ...
$ hledger rewrite ^income --add-posting '(liabilities:tax) *.33'
$ hledger rewrite expenses:gifts --add-posting '(budget:gifts) *-1"'
$ hledger rewrite ^income --add-posting '(budget:foreign currency) *0.25 JPY; diversify'
Argument for --add-posting option is a usual posting of transaction
with an exception for amount specification. More precisely, you can use
'*' (star symbol) before the amount to indicate that that this is a
factor for an amount of original matched posting. If the amount includes
a commodity name, the new posting amount will be in the new commodity;
otherwise, it will be in the matched posting amount's commodity.
Re-write rules in a file
During the run this tool will execute so called "Automated Transactions" found in any journal it process. I.e instead of specifying this operations in command line you can put them in a journal file.
$ rewrite-rules.journal
Make contents look like this:
= ^income
(liabilities:tax) *.33
= expenses:gifts
budget:gifts *-1
assets:budget *1
Note that '=' (equality symbol) that is used instead of date in
transactions you usually write. It indicates the query by which you want
to match the posting to add new ones.
$ hledger rewrite -f input.journal -f rewrite-rules.journal > rewritten-tidy-output.journal
This is something similar to the commands pipeline:
$ hledger rewrite -f input.journal '^income' --add-posting '(liabilities:tax) *.33' \
| hledger rewrite -f - expenses:gifts --add-posting 'budget:gifts *-1' \
--add-posting 'assets:budget *1' \
> rewritten-tidy-output.journal
It is important to understand that relative order of such entries in journal is important. You can re-use result of previously added postings.
Diff output format
To use this tool for batch modification of your journal files you may find useful output in form of unified diff.
$ hledger rewrite --diff -f examples/sample.journal '^income' --add-posting '(liabilities:tax) *.33'
Output might look like:
--- /tmp/examples/sample.journal
+++ /tmp/examples/sample.journal
@@ -18,3 +18,4 @@
2008/01/01 income
- assets:bank:checking $1
+ assets:bank:checking $1
income:salary
+ (liabilities:tax) 0
@@ -22,3 +23,4 @@
2008/06/01 gift
- assets:bank:checking $1
+ assets:bank:checking $1
income:gifts
+ (liabilities:tax) 0
If you'll pass this through patch tool you'll get transactions
containing the posting that matches your query be updated. Note that
multiple files might be update according to list of input files
specified via --file options and include directives inside of these
files.
Be careful. Whole transaction being re-formatted in a style of output
from hledger print.
See also:
https://github.com/simonmichael/hledger/issues/99
rewrite vs. print --auto
This command predates print --auto, and currently does much the same thing, but with these differences:
-
with multiple files, rewrite lets rules in any file affect all other files. print --auto uses standard directive scoping; rules affect only child files.
-
rewrite's query limits which transactions can be rewritten; all are printed. print --auto's query limits which transactions are printed.
-
rewrite applies rules specified on command line or in the journal. print --auto applies rules specified in the journal.
Maintenance commands
check
Check for various kinds of errors in your data.
Flags:
no command-specific flags
hledger provides a number of built-in correctness checks to help
validate your data and prevent errors. Some are run automatically, some
when you enable --strict mode; or you can run any of them on demand by
providing them as arguments to the check command. check produces no
output and a zero exit code if all is well. Eg:
hledger check # run basic checks
hledger check -s # run basic and strict checks
hledger check ordereddates payees # run basic checks and two others
If you are an Emacs user, you can also configure flycheck-hledger to run these checks, providing instant feedback as you edit the journal.
Here are the checks currently available. They are generally checked in the order they are shown here, and only the first failure will be reported.
Basic checks
These important checks are performed by default, by almost all hledger commands:
-
parseable - data files are in a supported format, with no syntax errors and no invalid include directives. This ensures that all files exist and are readable.
-
autobalanced - all transactions are balanced, after automatically inferring missing amounts and conversion rates and then converting amounts to cost. This ensures that each transaction's journal entry is well formed.
-
assertions - all balance assertions in the journal are passing. Balance assertions are a strong defense against errors, catching many problems. This check is on by default, but if it gets in your way, you can disable it temporarily with
-I/--ignore-assertions, or as a default by adding that flag to your config file. (Then use-s/--strictorhledger check assertionswhen you want to enable it).
Strict checks
When the -s/--strict flag is used (AKA strict mode),
all commands will perform the following additional checks (and
assertions, above). These provide extra error-catching power to help
you keep your data clean and correct:
-
balanced - like
autobalanced, but implicit conversions between commodities are not allowed; all conversion transactions must use cost notation or equity postings. This prevents wrong conversions caused by typos. -
commodities - all commodity symbols used must be declared. This guards against mistyping or omitting commodity symbols.
-
accounts - all account names used must be declared. This prevents the use of mis-spelled or outdated account names.
Other checks
These are not wanted by everyone, but can be run using the check
command:
-
tags - all tags used must be declared. This prevents mis-spelled tag names. Note hledger fairly often finds unintended tags in comments.
-
payees - all payees used in transactions must be declared. This will force you to declare any new payee name before using it. Most people will probably find this a bit too strict.
-
ordereddates - within each file, transactions must be ordered by date. This is a simple and effective error catcher. It's not included in strict mode, but you can add it by running
hledger check -s ordereddates. If enabled, this check is performed before balance assertions. -
recentassertions - all accounts with balance assertions must have one that's within the 7 days before their latest posting. This will encourage adding balance assertions for your active asset/liability accounts, which in turn should encourage you to reconcile regularly with those real world balances - another strong defense against errors. (
hledger close --assert >>$LEDGER_FILEis a convenient way to add new balance assertions. Later these become quite redundant, and you might choose to remove them to reduce clutter.) -
uniqueleafnames - no two accounts may have the same last account name part (eg the
checkinginassets:bank:checking). This ensures each account can be matched by a unique short name, easier to remember and to type.
Custom checks
You can build your own custom checks with add-on command scripts. See also Cookbook > Scripting. Here are some examples from hledger/bin/:
-
hledger-check-tagfiles - all tag values containing
/exist as file paths -
hledger-check-fancyassertions - more complex balance assertions are passing
diff
Compares a particular account's transactions in two input files. It shows any transactions to this account which are in one file but not in the other.
Flags:
no command-specific flags
More precisely: for each posting affecting this account in either file, this command looks for a corresponding posting in the other file which posts the same amount to the same account (ignoring date, description, etc).
Since it compares postings, not transactions, this also works when multiple bank transactions have been combined into a single journal entry.
This command is useful eg if you have downloaded an account's transactions from your bank (eg as CSV data): when hledger and your bank disagree about the account balance, you can compare the bank data with your journal to find out the cause.
Examples:
$ hledger diff -f $LEDGER_FILE -f bank.csv assets:bank:giro
These transactions are in the first file only:
2014/01/01 Opening Balances
assets:bank:giro EUR ...
...
equity:opening balances EUR -...
These transactions are in the second file only:
setup
Check the status of the hledger installation.
Flags:
no command-specific flags
setup tests your hledger installation and prints a list of results,
sometimes with helpful hints. This is a good first command to run after
installing hledger. Also after upgrading, or when something's not
working, or just when you want a reminder of where things are.
It makes one network request to detect the latest hledger release version. It's ok if this fails or times out. It will use ANSI color by default, unless disabled by NO_COLOR or --color=n. It does not use a pager or a config file.
It expects that the hledger version you are running is installed in your PATH. If not, it will stop until you have done that (to keep things simple).
Example:
$ hledger setup
Checking your hledger setup..
Legend: good, neutral, unknown, warning
hledger
* is a released version ? no hledger 1.42.99-gbca4b39c5-20250425, mac-aarch64
* is up to date ? yes 1.42.99 installed, latest is 1.42.1
* is a native binary for this machine ? yes aarch64
* is installed in PATH ? yes /Users/simon/.local/bin/hledger
* has a system text encoding configured ? yes UTF-8, data files should use this encoding
* has a user config file ? (optional) no
* current directory has a local config ? yes /Users/simon/src/hledger/hledger.conf
* the config file is readable ? yes /Users/simon/src/hledger/hledger.conf
terminal
* the NO_COLOR variable is defined ? no
* --color is configured by config file ? no
* hledger will use color by default ? yes
* the PAGER variable is defined ? yes less
* --pager is configured by config file ? no
* hledger will use a pager when needed ? yes /opt/homebrew/bin/less
* the LESS variable is defined ? yes
* the HLEDGER_LESS variable is defined ? no
* adjusting LESS variable for color etc. ? yes
* --pretty is enabled by config file ? no tables will use ASCII characters
* bash shell completions are installed ? ?
* zsh shell completions are installed ? ?
journal
* the LEDGER_FILE variable is defined ? yes /Users/simon/finance/2025/2025.journal
* a default journal file is readable ? yes /Users/simon/finance/2025/2025.journal
* it includes additional files ? yes 15
* all commodities are declared ? yes 10
* all accounts are declared ? yes 160
* all accounts have types ? no 14 untyped
* accounts of each type were detected ? yes ALERXCV
* commodities/accounts are checked ? no use -s to check commodities/accounts
* balance assertions are checked ? yes use -I to ignore assertions
test
Run built-in unit tests.
Flags:
no command-specific flags
This command runs the unit tests built in to hledger and hledger-lib, printing the results on stdout. If any test fails, the exit code will be non-zero.
This is mainly used by hledger developers, but you can also use it to sanity-check the installed hledger executable on your platform. All tests are expected to pass - if you ever see a failure, please report as a bug!
Any arguments before a -- argument will be passed to the tasty test
runner as test-selecting -p patterns, and any arguments after -- will
be passed to tasty unchanged.
Examples:
$ hledger test # run all unit tests
$ hledger test balance # run tests with "balance" in their name
$ hledger test -- -h # show tasty's options
PART 5: COMMON TASKS
Here are some quick examples of how to do some basic tasks with hledger.
Getting help
Here's how to list commands and view options and command docs:
$ hledger # show available commands
$ hledger --help # show common options
$ hledger CMD --help # show CMD's options, common options and CMD's documentation
You can also view your hledger version's manual in several formats by using the help command. Eg:
$ hledger help # show the hledger manual with info, man or $PAGER (best available)
$ hledger help journal # show the journal topic in the hledger manual
$ hledger help --help # find out more about the help command
To view manuals and introductory docs on the web, visit https://hledger.org. Chat and mail list support and discussion archives can be found at https://hledger.org/support.
Constructing command lines
hledger has a flexible command line interface. We strive to keep it simple and ergonomic, but if you run into one of the sharp edges described in OPTIONS, here are some tips that might help:
- command-specific options must go after the command (it's fine to put
common options there too:
hledger CMD OPTS ARGS) - you can run addon commands via hledger (
hledger ui [ARGS]) or directly (hledger-ui [ARGS]) - enclose "problematic" arguments in single quotes
- if needed, also add a backslash to hide regular expression metacharacters from the shell
- to see how a misbehaving command line is being parsed, add
--debug=2.
Starting a journal file
hledger looks for your accounting data in a journal file,
$HOME/.hledger.journal by default:
$ hledger stats
The hledger journal file "/Users/simon/.hledger.journal" was not found.
Please create it first, eg with "hledger add" or a text editor.
Or, specify an existing journal file with -f or LEDGER_FILE.
You can override this by setting the LEDGER_FILE environment variable
(see below). It's a good practice to keep this important file under
version control, and to start a new file each year. So you could do
something like this:
$ mkdir ~/finance
$ cd ~/finance
$ git init
Initialized empty Git repository in /Users/simon/finance/.git/
$ touch 2023.journal
$ echo "export LEDGER_FILE=$HOME/finance/2023.journal" >> ~/.profile
$ source ~/.profile
$ hledger stats
Main file : /Users/simon/finance/2023.journal
Included files :
Transactions span : to (0 days)
Last transaction : none
Transactions : 0 (0.0 per day)
Transactions last 30 days: 0 (0.0 per day)
Transactions last 7 days : 0 (0.0 per day)
Payees/descriptions : 0
Accounts : 0 (depth 0)
Commodities : 0 ()
Market prices : 0 ()
Setting LEDGER_FILE
Set LEDGER_FILE on unix
It depends on your shell, but running these commands in the terminal will work for many people; adapt if needed:
$ echo 'export LEDGER_FILE=~/finance/main.journal' >> ~/.profile
$ source ~/.profile
When correctly configured:
env | grep LEDGER_FILEwill show your new setting- and so should
hledger setupandhledger files.
Set LEDGER_FILE on mac
In a terminal window, follow the unix procedure above.
Also, this optional step may be helpful for GUI applications:
-
Add an entry to
~/.MacOSX/environment.plistlike{ "LEDGER_FILE" : "~/finance/main.journal" } -
Run
killall Dockin a terminal window (or restart the machine), to complete the change.
When correctly configured for GUI applications:
- apps started from the dock or a spotlight search, such as a GUI Emacs, will be aware of the new LEDGER_FILE setting.
Set LEDGER_FILE on Windows
It can be easier to create a default file at
C:\Users\USER\.hledger.journal, and have it
include your other files. See I'm on
Windows, how do I keep my files in
AppData\Roaming ?
Otherwise: using the gui is easiest:
- In task bar, search for
environment variables, and choose "Edit environment variables for your account". - Create or change a
LEDGER_FILEsetting in the User variables pane. A typical value would beC:\Users\USER\finance\main.journal. - Click OK to complete the change.
- And open a new powershell window. (Existing windows won't see the change.)
Or at the command line, you can do it this way:
- In a powershell window, run
[Environment]::SetEnvironmentVariable("LEDGER_FILE", "C:\User\USER\finance\main.journal", [System.EnvironmentVariableTarget]::User) - And open a new powershell window. (Existing windows won't see the change.)
Warning, doing this from the Windows command line can be tricky; other methods you may find online:
- may not affect the current window
- may not be persistent
- may not work unless you are an administrator
- may limit values to 1024 characters
- may break dynamic references to other variables
- may require a new-enough version of powershell
- or may be intended for the older command window.
- If you still have trouble, see eg Setting Windows PowerShell environment variables or Adding path permanently to windows using powershell doesn't appear to work.
When correctly configured:
- in a new powershell window,
$env:LEDGER_FILEwill show your new setting - and so should
hledger setupand (once the file exists)hledger files.
Setting opening balances
Pick a starting date for which you can look up the balances of some real-world assets (bank accounts, wallet..) and liabilities (credit cards..).
To avoid a lot of data entry, you may want to start with just one or two accounts, like your checking account or cash wallet; and pick a recent starting date, like today or the start of the week. You can always come back later and add more accounts and older transactions, eg going back to january 1st.
Add an opening balances transaction to the journal, declaring the balances on this date. Here are two ways to do it:
-
The first way: open the journal in any text editor and save an entry like this:
2023-01-01 * opening balances assets:bank:checking $1000 = $1000 assets:bank:savings $2000 = $2000 assets:cash $100 = $100 liabilities:creditcard $-50 = $-50 equity:opening/closing balancesThese are start-of-day balances, ie whatever was in the account at the end of the previous day.
The * after the date is an optional status flag. Here it means "cleared & confirmed".
The currency symbols are optional, but usually a good idea as you'll be dealing with multiple currencies sooner or later.
The = amounts are optional balance assertions, providing extra error checking.
-
The second way: run
hledger addand follow the prompts to record a similar transaction:$ hledger add Adding transactions to journal file /Users/simon/finance/2023.journal Any command line arguments will be used as defaults. Use tab key to complete, readline keys to edit, enter to accept defaults. An optional (CODE) may follow transaction dates. An optional ; COMMENT may follow descriptions or amounts. If you make a mistake, enter < at any prompt to go one step backward. To end a transaction, enter . when prompted. To quit, enter . at a date prompt or press control-d or control-c. Date [2023-02-07]: 2023-01-01 Description: * opening balances Account 1: assets:bank:checking Amount 1: $1000 Account 2: assets:bank:savings Amount 2 [$-1000]: $2000 Account 3: assets:cash Amount 3 [$-3000]: $100 Account 4: liabilities:creditcard Amount 4 [$-3100]: $-50 Account 5: equity:opening/closing balances Amount 5 [$-3050]: Account 6 (or . or enter to finish this transaction): . 2023-01-01 * opening balances assets:bank:checking $1000 assets:bank:savings $2000 assets:cash $100 liabilities:creditcard $-50 equity:opening/closing balances $-3050 Save this transaction to the journal ? [y]: Saved. Starting the next transaction (. or ctrl-D/ctrl-C to quit) Date [2023-01-01]: .
If you're using version control, this could be a good time to commit the journal. Eg:
$ git commit -m 'initial balances' 2023.journal
Recording transactions
As you spend or receive money, you can record these transactions using one of the methods above (text editor, hledger add) or by using the hledger-iadd or hledger-web add-ons, or by using the import command to convert CSV data downloaded from your bank.
Here are some simple transactions, see the hledger_journal(5) manual and hledger.org for more ideas:
2023/1/10 * gift received
assets:cash $20
income:gifts
2023.1.12 * farmers market
expenses:food $13
assets:cash
2023-01-15 paycheck
income:salary
assets:bank:checking $1000
Reconciling
Periodically you should reconcile - compare your hledger-reported balances against external sources of truth, like bank statements or your bank's website - to be sure that your ledger accurately represents the real-world balances (and, that the real-world institutions have not made a mistake!). This gets easy and fast with (1) practice and (2) frequency. If you do it daily, it can take 2-10 minutes. If you let it pile up, expect it to take longer as you hunt down errors and discrepancies.
A typical workflow:
-
Reconcile cash. Count what's in your wallet. Compare with what hledger reports (
hledger bal cash). If they are different, try to remember the missing transaction, or look for the error in the already-recorded transactions. A register report can be helpful (hledger reg cash). If you can't find the error, add an adjustment transaction. Eg if you have $105 after the above, and can't explain the missing $2, it could be:2023-01-16 * adjust cash assets:cash $-2 = $105 expenses:misc -
Reconcile checking. Log in to your bank's website. Compare today's (cleared) balance with hledger's cleared balance (
hledger bal checking -C). If they are different, track down the error or record the missing transaction(s) or add an adjustment transaction, similar to the above. Unlike the cash case, you can usually compare the transaction history and running balance from your bank with the one reported byhledger reg checking -C. This will be easier if you generally record transaction dates quite similar to your bank's clearing dates. -
Repeat for other asset/liability accounts.
Tip: instead of the register command, use hledger-ui to see a
live-updating register while you edit the journal:
hledger-ui --watch --register checking -C
After reconciling, it could be a good time to mark the reconciled
transactions' status as "cleared and confirmed", if you want to track
that, by adding the * marker. Eg in the paycheck transaction above,
insert * between 2023-01-15 and paycheck
If you're using version control, this can be another good time to commit:
$ git commit -m 'txns' 2023.journal
Reporting
Here are some basic reports.
Show all transactions:
$ hledger print
2023-01-01 * opening balances
assets:bank:checking $1000
assets:bank:savings $2000
assets:cash $100
liabilities:creditcard $-50
equity:opening/closing balances $-3050
2023-01-10 * gift received
assets:cash $20
income:gifts
2023-01-12 * farmers market
expenses:food $13
assets:cash
2023-01-15 * paycheck
income:salary
assets:bank:checking $1000
2023-01-16 * adjust cash
assets:cash $-2 = $105
expenses:misc
Show account names, and their hierarchy:
$ hledger accounts --tree
assets
bank
checking
savings
cash
equity
opening/closing balances
expenses
food
misc
income
gifts
salary
liabilities
creditcard
Show all account totals:
$ hledger balance
$4105 assets
$4000 bank
$2000 checking
$2000 savings
$105 cash
$-3050 equity:opening/closing balances
$15 expenses
$13 food
$2 misc
$-1020 income
$-20 gifts
$-1000 salary
$-50 liabilities:creditcard
--------------------
0
Show only asset and liability balances, as a flat list, limited to depth 2:
$ hledger bal assets liabilities -2
$4000 assets:bank
$105 assets:cash
$-50 liabilities:creditcard
--------------------
$4055
Show the same thing without negative numbers, formatted as a simple balance sheet:
$ hledger bs -2
Balance Sheet 2023-01-16
|| 2023-01-16
========================++============
Assets ||
------------------------++------------
assets:bank || $4000
assets:cash || $105
------------------------++------------
|| $4105
========================++============
Liabilities ||
------------------------++------------
liabilities:creditcard || $50
------------------------++------------
|| $50
========================++============
Net: || $4055
The final total is your "net worth" on the end date. (Or use bse for
a full balance sheet with equity.)
Show income and expense totals, formatted as an income statement:
hledger is
Income Statement 2023-01-01-2023-01-16
|| 2023-01-01-2023-01-16
===============++=======================
Revenues ||
---------------++-----------------------
income:gifts || $20
income:salary || $1000
---------------++-----------------------
|| $1020
===============++=======================
Expenses ||
---------------++-----------------------
expenses:food || $13
expenses:misc || $2
---------------++-----------------------
|| $15
===============++=======================
Net: || $1005
The final total is your net income during this period.
Show transactions affecting your wallet, with running total:
$ hledger register cash
2023-01-01 opening balances assets:cash $100 $100
2023-01-10 gift received assets:cash $20 $120
2023-01-12 farmers market assets:cash $-13 $107
2023-01-16 adjust cash assets:cash $-2 $105
Show weekly posting counts as a bar chart:
$ hledger activity -W
2019-12-30 *****
2023-01-06 ****
2023-01-13 ****
Migrating to a new file
At the end of the year, you may want to continue your journal in a new file, so that old transactions don't slow down or clutter your reports, and to help ensure the integrity of your accounting history. See the close command.
If using version control, don't forget to git add the new file.
BUGS
We welcome bug reports in the hledger issue tracker (https://bugs.hledger.org), or on the hledger chat or mail list (https://hledger.org/support).
Some known issues and limitations:
hledger uses the system's text encoding when reading non-ascii text. If no system encoding is configured, or if the data's encoding is different, hledger will give an error. (See Text encoding, Troubleshooting.)
On Microsoft Windows, depending what kind of terminal window you use, non-ascii characters, ANSI text formatting, and/or the add command's TAB key, may not be fully supported. (For best results, try a powershell window.)
When processing large data files, hledger uses more memory than Ledger.
Troubleshooting
Here are some common issues you might encounter when you run hledger, and how to resolve them (and remember also you can usually get quick Support):
PATH issues: I get an error like "No command 'hledger' found"
Depending how you installed hledger, the executables may not be in your
shell's PATH. Eg on unix systems, stack installs hledger in
~/.local/bin and cabal installs it in ~/.cabal/bin. You may need to
add one of these directories to your shell's PATH, and/or open a new
terminal window.
LEDGER_FILE issues: I configured LEDGER_FILE but hledger is not using it\
LEDGER_FILEshould be a real environment variable, not just a shell variable. Eg on unix, the commandenv | grep LEDGER_FILEshould show it. You may need to useexport(see https://stackoverflow.com/a/7411509). On Windows,$env:LEDGER_FILEshould show it.- You may need to force your shell to see the new configuration. A simple way is to close your terminal window and open a new one.
Text decoding issues: I get errors like "Illegal byte sequence" or
"Invalid or incomplete multibyte or wide character" or
"commitAndReleaseBuffer: invalid argument (invalid character)"
hledger usually needs its input to be decodable with the system
locale's text encoding. See Text encoding and
Install: Text encoding.
COMPATIBILITY ISSUES: hledger gives an error with my Ledger file
Not all of Ledger's journal file syntax or feature set is supported.
See hledger and Ledger for full details.
hledger-ui
NAME
hledger-ui - terminal interface (TUI) for hledger, a robust, friendly
plain text accounting app.
SYNOPSIS
hledger-ui [OPTS] [QUERYARGS]
orhledger ui [OPTS] [QUERYARGS]
DESCRIPTION
This manual is for hledger's terminal interface, version 1.52. See also the hledger manual for common concepts and file formats.
hledger is a robust, user-friendly, cross-platform set of programs for tracking money, time, or any other commodity, using double-entry accounting and a simple, editable file format. hledger is inspired by and largely compatible with ledger(1), and largely interconvertible with beancount(1).
hledger-ui is hledger's terminal interface, providing an efficient full-window text UI for viewing accounts and transactions, and some limited data entry capability. It is easier than hledger's command-line interface, and sometimes quicker and more convenient than the web interface.
Like hledger, it reads from (and appends to) a journal file specified by
the LEDGER_FILE environment variable (defaulting to
$HOME/.hledger.journal); or you can specify files with -f options.
It can also read timeclock files, timedot files, or any CSV/SSV/TSV file
with a date field. (See hledger(1) -> Input for details.)
Unlike hledger, hledger-ui hides all future-dated transactions by default. They can be revealed, along with any rule-generated periodic transactions, by pressing the F key (or starting with --forecast) to enable "forecast mode".
OPTIONS
Any arguments are interpreted as a hledger query which filters the data. hledger-ui provides the following options:
Flags:
-w --watch watch for data and date changes and reload
automatically
--theme=THEME use this custom display theme (light,
dark, terminal, greenterm)
--cash start in the cash accounts screen
--bs start in the balance sheet accounts screen
--is start in the income statement accounts screen
--all start in the all accounts screen
--register=ACCTREGEX start in the (first matched) account's register
--change show period balances (changes) at startup instead
of historical balances
-l --flat show accounts as a flat list (default)
-t --tree show accounts as a tree
and also supports many of hledger's general options:
General input/data transformation flags:
-f --file=[FMT:]FILE Read data from FILE, or from stdin if FILE is -,
inferring format from extension or a FMT: prefix.
Can be specified more than once. If not specified,
reads from $LEDGER_FILE or $HOME/.hledger.journal.
--rules=RULESFILE Use rules defined in this rules file for
converting subsequent CSV/SSV/TSV files. If not
specified, uses FILE.csv.rules for each FILE.csv.
--alias=A=B|/RGX/=RPL transform account names from A to B, or by
replacing regular expression matches
--auto generate extra postings by applying auto posting
rules ("=") to all transactions
--forecast[=PERIOD] Generate extra transactions from periodic rules
("~"), from after the latest ordinary transaction
until 6 months from now. Or, during the specified
PERIOD (the equals is required). Auto posting rules
will also be applied to these transactions. In
hledger-ui, also make future-dated transactions
visible at startup.
-I --ignore-assertions don't check balance assertions by default
--txn-balancing=... how to check that transactions are balanced:
'old': use global display precision
'exact': use transaction precision (default)
--infer-costs infer conversion equity postings from costs
--infer-equity infer costs from conversion equity postings
--infer-market-prices infer market prices from costs
--pivot=TAGNAME use a different field or tag as account names
-s --strict do extra error checks (and override -I)
--verbose-tags add tags indicating generated/modified data
General output/reporting flags (supported by some commands):
-b --begin=DATE include postings/transactions on/after this date
-e --end=DATE include postings/transactions before this date
(with a report interval, will be adjusted to
following subperiod end)
-D --daily multiperiod report with 1 day interval
-W --weekly multiperiod report with 1 week interval
-M --monthly multiperiod report with 1 month interval
-Q --quarterly multiperiod report with 1 quarter interval
-Y --yearly multiperiod report with 1 year interval
-p --period=PERIODEXP set begin date, end date, and/or report interval,
with more flexibility
--today=DATE override today's date (affects relative dates)
--date2 match/use secondary dates instead (deprecated)
-U --unmarked include only unmarked postings/transactions
-P --pending include only pending postings/transactions
-C --cleared include only cleared postings/transactions
(-U/-P/-C can be combined)
-R --real include only non-virtual postings
-E --empty Show zero items, which are normally hidden.
In hledger-ui & hledger-web, do the opposite.
--depth=DEPTHEXP if a number (or -NUM): show only top NUM levels
of accounts. If REGEXP=NUM, only apply limiting to
accounts matching the regular expression.
-B --cost show amounts converted to their cost/sale amount
-V --market Show amounts converted to their value at period
end(s) in their default valuation commodity.
Equivalent to --value=end.
-X --exchange=COMM Show amounts converted to their value at period
end(s) in the specified commodity.
Equivalent to --value=end,COMM.
--value=WHEN[,COMM] show amounts converted to their value on the
specified date(s) in their default valuation
commodity or a specified commodity. WHEN can be:
'then': value on transaction dates
'end': value at period end(s)
'now': value today
YYYY-MM-DD: value on given date
-c --commodity-style=S Override a commodity's display style.
Eg: -c '.' or -c '1.000,00 EUR'
--pretty[=YN] Use box-drawing characters in text output? Can be
'y'/'yes' or 'n'/'no'.
If YN is specified, the equals is required.
General help flags:
-h --help show command line help
--tldr show command examples with tldr
--info show the manual with info
--man show the manual with man
--version show version information
--debug=[1-9] show this much debug output (default: 1)
--pager=YN use a pager when needed ? y/yes (default) or n/no
--color=YNA --colour use ANSI color ? y/yes, n/no, or auto (default)
With hledger-ui, the --debug option sends debug output to a
hledger-ui.log file in the current directory.
If you use the bash shell, you can auto-complete flags by pressing TAB in the command line. If this is not working see Install > Shell completions.
MOUSE
In most modern terminals, you can navigate through the screens with a mouse or touchpad:
- Use mouse wheel or trackpad to scroll up and down
- Click on list items to go deeper
- Click on the left margin (column 0) to go back.
KEYS
Keyboard gives more control.
? shows a help dialog listing all keys. (Some of these also appear in
the quick help at the bottom of each screen.) Press ? again (or
ESCAPE, or LEFT, or q) to close it. The following keys work on
most screens:
The cursor keys navigate: RIGHT or ENTER goes deeper, LEFT returns
to the previous screen, UP/DOWN/PGUP/PGDN/HOME/END move up
and down through lists. J/K jump down/up 10 items at a time.
Emacs-style (CTRL-p/CTRL-n/CTRL-f/CTRL-b) and VI-style
(k,j,l,h) movement keys are also supported.
(Tip: movement speed is limited by your keyboard repeat rate, to move faster you may want to adjust it. On a mac, the Karabiner app is one way to do that.)
/ lets you set a general filter query limiting the data shown, using
the same query terms as in hledger and
hledger-web. While editing the query, you can use CTRL-a/e/d/k, BS,
cursor
keys;
press ENTER to set it, or ESCAPEto cancel. There are also keys for
quickly adjusting some common filters like account depth and transaction
status (see below). BACKSPACE or DELETE removes all filters, showing
all transactions.
As mentioned above, by default hledger-ui hides future transactions -
both ordinary transactions recorded in the journal, and periodic
transactions generated by rule. F toggles forecast mode, in which
future/forecasted transactions are shown.
Pressing SHIFT-DOWN narrows the report period, and pressing SHIFT-UP
expands it again. When narrowed, the current report period is displayed
in the header line, pressing SHIFT-LEFT or SHIFT-RIGHT moves to the
previous or next period, and pressing T sets the period to "today".
If you are using -w/--watch and viewing a narrowed period containing
today, the view will follow any changes in system date (moving to the
period containing the new date). (These keys work only with the standard
Julian calendar year/quarter/month/week/day periods; they are not
affected by a custom report interval specified at the command line.)
You can also specify a non-standard period with / and a date: query;
in this case, the period is not movable with the arrow keys.
(Tip: arrow keys with Shift do not work out of the box in all terminal
software. Eg in Apple's Terminal, the SHIFT-DOWN and SHIFT-UP keys must
be configured as follows: in Terminal's preferences, click Profiles,
select your current profile on the left, click Keyboard on the right,
click + and add this for SHIFT-DOWN: \033[1;2B, click + and add this
for SHIFT-UP: \033[1;2A. In other terminals (Windows Terminal ?) you
might need to configure SHIFT-RIGHT and SHIFT-LEFT to emit \033[1;2C
and \033[1;2D respectively.)
ESCAPE resets the UI state and jumps back to the top screen, restoring
the app's initial state at startup. Or, it cancels minibuffer data
entry or the help dialog.
CTRL-l redraws the screen and centers the selection if possible
(selections near the top won't be centered, since we don't scroll
above the top).
g reloads from the data file(s) and updates the current screen and any
previous screens. (With large files, this could cause a noticeable
pause.)
I toggles balance assertion checking. Disabling balance assertions
temporarily can be useful for troubleshooting. (If hledger-ui was
started with a --pivot option, re-enabling balance assertions with the
I key also reloads the journal, like g.)
a runs command-line hledger's add command, and reloads the updated
file. This allows some basic data entry.
A is like a, but runs the
hledger-iadd tool,
which provides a terminal interface. This key will be available if
hledger-iadd is installed in $path.
E runs $HLEDGER_UI_EDITOR, or $EDITOR, or a default
(emacsclient -a "" -nw) on the journal file. With some editors (emacs,
vi), the cursor will be positioned at the current transaction when
invoked from the register and transaction screens, and at the error
location (if possible) when invoked from the error screen.
B toggles cost mode, showing amounts converted to their cost's
commodity (see hledger manual > Cost
reporting.
V toggles value mode, showing amounts converted to their market value
(see hledger manual > Valuation flag). More
specifically,
-
By default, the
Vkey toggles showing end value (--value=end) on or off. The valuation date will be the report end date if specified, otherwise today. -
If you started hledger-ui with some other valuation (such as
--value=then,EUR), theVkey toggles that off or on.
Cost/value tips: - When showing end value, you can change the report end
date without restarting, by pressing / and adding a query like
date:..YYYY-MM-DD. - Either cost mode, or value mode, can be active,
but not both at once. Cost mode takes precedence. - There's not yet any
visual indicator that cost or value mode is active, other than the
amount values.
q quits the application.
Additional screen-specific keys are described below.
SCREENS
At startup, hledger-ui shows a menu screen by default. From here you can
navigate to other screens using the cursor keys: UP/DOWN to select,
RIGHT to move to the selected screen, LEFT to return to the previous
screen. Or you can use ESC to return directly to the top menu screen.
You can also use a command line flag to specific a different startup
screen (--cs, --bs, --is, --all, or --register=ACCT).
Menu screen
This is the top-most screen. From here you can navigate to several screens listing accounts of various types. Note some of these may not show anything until you have configured account types.
Cash accounts screen
This screen shows "cash" (ie, liquid asset) accounts (like
hledger balancesheet type:c). It always shows balances (historical
ending balances on the date shown in the title line).
Balance sheet accounts screen
This screen shows asset, liability and equity accounts (like
hledger balancesheetequity). It always shows balances.
Income statement accounts screen
This screen shows revenue and expense accounts (like
hledger incomestatement). It always shows changes (balance changes in
the period shown in the title line).
All accounts screen
This screen shows all accounts in your journal (unless filtered by a
query; like hledger balance). It shows balances by default; you can
toggle showing changes with the H key.
Register screen
This screen shows the transactions affecting a particular account. Each line represents one transaction, and shows:
-
the other account(s) involved, in abbreviated form. (If there are both real and virtual postings, it shows only the accounts affected by real postings.)
-
the overall change to the current account's balance; positive for an inflow to this account, negative for an outflow.
-
the running total after the transaction. With the
Hkey you can toggle between- the period total, which is from just the transactions displayed
- or the historical total, which includes any undisplayed transactions before the start of the report period (and matching the filter query if any). This will be the running historical balance (what you would see on a bank's website, eg) if not disturbed by a query.
Note, this screen combines each transaction's in-period postings to a
single line item, dated with the earliest in-period transaction or
posting date (like hledger's aregister). So custom posting dates can
cause the running balance to be temporarily inaccurate. (See hledger
manual > aregister and posting
dates.)
Transactions affecting this account's subaccounts will be included in
the register if the accounts screen is in tree mode, or if it's in list
mode but this account has subaccounts which are not shown due to a depth
limit. In other words, the register always shows the transactions
contributing to the balance shown on the accounts screen. Tree mode/list
mode can be toggled with t here also.
U toggles filtering by unmarked status, showing
or hiding unmarked transactions. Similarly, P toggles pending
transactions, and C toggles cleared transactions. (By default,
transactions with all statuses are shown; if you activate one or two
status filters, only those transactions are shown; and if you activate
all three, the filter is removed.)
R toggles real mode, in which virtual
postings are ignored.
z toggles nonzero mode, in which only transactions posting a nonzero
change are shown (hledger-ui shows zero items by default, unlike
command-line hledger).
Press RIGHT to view the selected transaction in detail.
Transaction screen
This screen shows a single transaction, as a general journal entry, similar to hledger's print command and journal format (hledger_journal(5)).
The transaction's date(s) and any cleared flag, transaction code, description, comments, along with all of its account postings are shown. Simple transactions have two postings, but there can be more (or in certain cases, fewer).
UP and DOWN will step through all transactions listed in the
previous account register screen. In the title bar, the numbers in
parentheses show your position within that account register. They will
vary depending on which account register you came from (remember most
transactions appear in multiple account registers). The #N number
preceding them is the transaction's position within the complete
unfiltered journal, which is a more stable id (at least until the next
reload).
On this screen (and the register screen), the E key will open your
text editor with the cursor positioned at the current transaction if
possible.
Error screen
This screen will appear if there is a problem, such as a parse error, when you press g to reload. Once you have fixed the problem, press g again to reload and resume normal operation. (Or, you can press escape to cancel the reload attempt.)
WATCH MODE
One of hledger-ui's best features is the auto-reloading -w/--watch
mode. With this flag, it will update the display automatically whenever
changes are saved to the data files.
This is very useful when reconciling. A good workflow is to have your bank's online register open in a browser window, for reference; the journal file open in an editor window; and hledger-ui in watch mode in a terminal window, eg:
$ hledger-ui --watch --register checking -C
As you mark things cleared in the editor, you can see the effect immediately without having to context switch. This leaves more mental bandwidth for your accounting. Of course you can still interact with hledger-ui when needed, eg to toggle cleared mode, or to explore the history.
--watch problems
However. There are limitations/unresolved bugs with --watch:
- It may not work at all for you, depending on platform or system configuration. On some unix systems, increasing fs.inotify.max_user_watches or fs.file-max parameters in /etc/sysctl.conf might help. (#836)
- It may not detect changes made from outside a virtual machine, ie by an editor running on the host system.
- It may not detect file changes on certain less common filesystems.
- It may use increasing CPU and RAM over time, especially with large
files. (This is probably not --watch specific, you may be able to
reproduce it by pressing
grepeatedly.) (#1825)
Tips/workarounds:
- If --watch won't work for you, press
gto reload data manually instead. - If --watch is leaking resources over time, quit and restart (or suspend and resume) hledger-ui when you're not using it.
- When running hledger-ui inside a VM, also make file changes inside the VM.
- When working with files mounted from another machine, make sure the system clocks on both machines are roughly in agreement.
ENVIRONMENT
LEDGER_FILE The main journal file to use when not specified with
-f/--file. Default: $HOME/.hledger.journal.
BUGS
We welcome bug reports in the hledger issue tracker (https://bugs.hledger.org), or on the hledger chat or mail list (https://hledger.org/support).
Some known issues:
-f- doesn't work (hledger-ui can't read from stdin).
--watch is not robust, especially with large files (see WATCH MODE
above).
If you press g with large files, there could be a noticeable pause
with the UI unresponsive.
hledger-web
NAME
hledger-web - web interface and API for hledger, a robust, friendly
plain text accounting app.
SYNOPSIS
hledger-web [OPTS] [QUERY]
orhledger web [OPTS] [QUERY]
DESCRIPTION
This manual is for hledger's web interface, version 1.52. See also the hledger manual for common concepts and file formats.
hledger is a robust, user-friendly, cross-platform set of programs for tracking money, time, or any other commodity, using double-entry accounting and a simple, editable file format. hledger is inspired by and largely compatible with ledger(1), and largely interconvertible with beancount(1).
hledger-web is a simple web application for browsing and adding transactions. It provides a more user-friendly UI than the hledger CLI or hledger-ui TUI, showing more at once (accounts, the current account register, balance charts) and allowing history-aware data entry, interactive searching, and bookmarking.
hledger-web also lets you share a journal with multiple users, or even the public web. There is no access control, so if you need that you should put it behind a suitable web proxy. As a small protection against data loss when running an unprotected instance, it writes a numbered backup of the main journal file (only) on every edit.
Like hledger, it reads from (and appends to) a journal file specified by
the LEDGER_FILE environment variable (defaulting to
$HOME/.hledger.journal); or you can specify files with -f options.
It can also read timeclock files, timedot files, or any CSV/SSV/TSV file
with a date field. (See hledger(1) -> Input for details.)
hledger-web can be run in three modes:
-
--serve-browsemode (the default): the app serves the web UI and JSON API, and opens your default web browser to show the app if possible, and exits automatically after two minutes of inactivity (with no requests received and no open browser windows viewing it). -
--serve: the app just serves the web UI and JSON API. -
--serve-api: the app just serves the JSON API.
In all cases hledger-web runs as a foreground process, logging requests to stdout.
OPTIONS
hledger-web provides the following options:
Flags:
--serve --server serve and log requests, don't browse or auto-exit
--serve-api like --serve, but serve only the JSON web API,
not the web UI
--allow=view|add|edit set the user's access level for changing data
(default: `add`). It also accepts `sandstorm` for
use on that platform (reads permissions from the
`X-Sandstorm-Permissions` request header).
--cors=ORIGIN allow cross-origin requests from the specified
origin; setting ORIGIN to "*" allows requests from
any origin
--host=IPADDR listen on this IP address (default: 127.0.0.1)
--port=PORT listen on this TCP port (default: 5000)
--socket=SOCKET listen on the given unix socket instead of an IP
address and port (unix only; implies --serve)
--base-url=BASEURL set the base url (default: http://IPADDR:PORT)
--test run hledger-web's tests and exit. hspec test
runner args may follow a --, eg: hledger-web --test
--help
By default hledger-web listens only on IP address 127.0.0.1, which be
accessed only from the local machine.
To allow access from elsewhere, use --host to specify an externally
accessible address configured on this machine, The special address
0.0.0.0 causes it to listen on all of this machine's addresses.
Similarly, you can use --port to listen on a TCP port other than 5000.
This is useful if you want to run multiple hledger-web instances on a
machine.
When --socket is used, hledger-web creates and communicates via a
socket file instead of a TCP port. This can be more secure, respects
unix file permissions, and makes certain use cases easier, such as
running per-user instances behind an nginx reverse proxy. (Eg:
proxy_pass http://unix:/tmp/hledger/${remote_user}.socket;.)
You can use --base-url to change the protocol, hostname, port and path
that appear in hledger-web's hyperlinks. This is useful eg when
integrating hledger-web within a larger website. The default is
http://HOST:PORT/ using the server's configured host address and TCP
port (or http://HOST if PORT is 80). Note this affects url generation
but not route parsing.
hledger-web also supports many of hledger's general options:
General input/data transformation flags:
-f --file=[FMT:]FILE Read data from FILE, or from stdin if FILE is -,
inferring format from extension or a FMT: prefix.
Can be specified more than once. If not specified,
reads from $LEDGER_FILE or $HOME/.hledger.journal.
--rules=RULESFILE Use rules defined in this rules file for
converting subsequent CSV/SSV/TSV files. If not
specified, uses FILE.csv.rules for each FILE.csv.
--alias=A=B|/RGX/=RPL transform account names from A to B, or by
replacing regular expression matches
--auto generate extra postings by applying auto posting
rules ("=") to all transactions
--forecast[=PERIOD] Generate extra transactions from periodic rules
("~"), from after the latest ordinary transaction
until 6 months from now. Or, during the specified
PERIOD (the equals is required). Auto posting rules
will also be applied to these transactions. In
hledger-ui, also make future-dated transactions
visible at startup.
-I --ignore-assertions don't check balance assertions by default
--txn-balancing=... how to check that transactions are balanced:
'old': use global display precision
'exact': use transaction precision (default)
--infer-costs infer conversion equity postings from costs
--infer-equity infer costs from conversion equity postings
--infer-market-prices infer market prices from costs
--pivot=TAGNAME use a different field or tag as account names
-s --strict do extra error checks (and override -I)
--verbose-tags add tags indicating generated/modified data
General output/reporting flags (supported by some commands):
-b --begin=DATE include postings/transactions on/after this date
-e --end=DATE include postings/transactions before this date
(with a report interval, will be adjusted to
following subperiod end)
-D --daily multiperiod report with 1 day interval
-W --weekly multiperiod report with 1 week interval
-M --monthly multiperiod report with 1 month interval
-Q --quarterly multiperiod report with 1 quarter interval
-Y --yearly multiperiod report with 1 year interval
-p --period=PERIODEXP set begin date, end date, and/or report interval,
with more flexibility
--today=DATE override today's date (affects relative dates)
--date2 match/use secondary dates instead (deprecated)
-U --unmarked include only unmarked postings/transactions
-P --pending include only pending postings/transactions
-C --cleared include only cleared postings/transactions
(-U/-P/-C can be combined)
-R --real include only non-virtual postings
-E --empty Show zero items, which are normally hidden.
In hledger-ui & hledger-web, do the opposite.
--depth=DEPTHEXP if a number (or -NUM): show only top NUM levels
of accounts. If REGEXP=NUM, only apply limiting to
accounts matching the regular expression.
-B --cost show amounts converted to their cost/sale amount
-V --market Show amounts converted to their value at period
end(s) in their default valuation commodity.
Equivalent to --value=end.
-X --exchange=COMM Show amounts converted to their value at period
end(s) in the specified commodity.
Equivalent to --value=end,COMM.
--value=WHEN[,COMM] show amounts converted to their value on the
specified date(s) in their default valuation
commodity or a specified commodity. WHEN can be:
'then': value on transaction dates
'end': value at period end(s)
'now': value today
YYYY-MM-DD: value on given date
-c --commodity-style=S Override a commodity's display style.
Eg: -c '.' or -c '1.000,00 EUR'
--pretty[=YN] Use box-drawing characters in text output? Can be
'y'/'yes' or 'n'/'no'.
If YN is specified, the equals is required.
General help flags:
-h --help show command line help
--tldr show command examples with tldr
--info show the manual with info
--man show the manual with man
--version show version information
--debug=[1-9] show this much debug output (default: 1)
--pager=YN use a pager when needed ? y/yes (default) or n/no
--color=YNA --colour use ANSI color ? y/yes, n/no, or auto (default)
hledger-web shows accounts with zero balances by default (like
hledger-ui, and unlike hledger). Using the -E/--empty flag will
reverse this behaviour. If you see accounts which appear to have a zero
balance, but cannot be hidden with -E, it's because they have a
mixed-cost balance, which looks like zero when costs are hidden.
(hledger-web does not show costs.)
Reporting options and/or query arguments can be used to set an initial query, which although not shown in the UI, will restrict the data shown (in addition to any search query entered in the UI).
If you use the bash shell, you can auto-complete flags by pressing TAB in the command line. If this is not working see Install > Shell completions.
PERMISSIONS
By default, hledger-web allows anyone who can reach it to view the journal and to add new transactions, but not to change existing data.
You can restrict who can reach it, by
- setting the IP address it listens on (see
--hostabove). By default it listens on 127.0.0.1, accessible to users on the local machine only. - putting it behind an authenticating proxy, such as caddy or apache
- putting it behind a firewall
And you can restrict what the users reaching it can do, by specifying
the --allow=ACCESSLEVEL option at startup. ACCESSLEVEL is one of:
view- allows viewing the journal file(s)add- also allows adding new transactions to the main journal fileedit- also allows editing, uploading or downloading the journal file(s)sandstorm- (for the hledger-web Sandstorm app:) allows whichever ofview,add, oreditare specified in theX-Sandstorm-PermissionsHTTP header
The default access level is add.
EDITING, UPLOADING, DOWNLOADING
If you enable the manage capability mentioned above, you'll see a new
"spanner" button to the right of the search form. Clicking this will
let you edit, upload, or download the journal file or any files it
includes.
Note, unlike any other hledger command, in this mode you (or any visitor) can alter or wipe the data files.
Normally whenever a file is changed in this way, hledger-web saves a numbered backup (assuming file permissions allow it, the disk is not full, etc.) hledger-web is not aware of version control systems, currently; if you use one, you'll have to arrange to commit the changes yourself (eg with a cron job or a file watcher like entr).
Changes which would leave the journal file(s) unparseable or non-valid (eg with failing balance assertions) are prevented. (Probably. This needs re-testing.)
RELOADING
hledger-web detects changes made to the files by other means (eg if you edit it directly, outside of hledger-web), and it will show the new data when you reload the page or navigate to a new page. If a change makes a file unparseable, hledger-web will display an error message until the file has been fixed.
(Note: if you are viewing files mounted from another machine, make sure that both machine clocks are roughly in step.)
JSON API
In addition to the web UI, hledger-web also serves a JSON API that can
be used to get data or add new transactions. If you want the JSON API
only, you can use the --serve-api flag. Eg:
$ hledger-web -f examples/sample.journal --serve-api
...
You can get JSON data from these routes:
/version
/accountnames
/transactions
/prices
/commodities
/accounts
/accounttransactions/ACCOUNTNAME
Eg, all account names in the journal (similar to the accounts command). (hledger-web's JSON does not include newlines, here we use python to prettify it):
$ curl -s http://127.0.0.1:5000/accountnames | python -m json.tool
[
"assets",
"assets:bank",
"assets:bank:checking",
"assets:bank:saving",
"assets:cash",
"expenses",
"expenses:food",
"expenses:supplies",
"income",
"income:gifts",
"income:salary",
"liabilities",
"liabilities:debts"
]
Or all transactions:
$ curl -s http://127.0.0.1:5000/transactions | python -m json.tool
[
{
"tcode": "",
"tcomment": "",
"tdate": "2008-01-01",
"tdate2": null,
"tdescription": "income",
"tindex": 1,
"tpostings": [
{
"paccount": "assets:bank:checking",
"pamount": [
{
"acommodity": "$",
"aismultiplier": false,
"aprice": null,
...
Most of the JSON corresponds to hledger's data types; for details of what the fields mean, see the Hledger.Data.Json haddock docs and click on the various data types, eg Transaction. And for a higher level understanding, see the journal docs. There is also a basic OpenAPI specification.
In some cases there is outer JSON corresponding to a "Report" type. To
understand that, go to the Hledger.Web.Handler.MiscR
haddock
and look at the source for the appropriate handler to see what it
returns. Eg for /accounttransactions it's
getAccounttransactionsR,
returning a "accountTransactionsReport ...". Looking
up the
haddock for that we can see that /accounttransactions returns an
AccountTransactionsReport,
which consists of a report title and a list of
AccountTransactionsReportItem (etc).
You can add a new transaction to the journal with a PUT request to
/add, if hledger-web was started with the add capability (enabled by
default). The payload must be the full, exact JSON representation of a
hledger transaction (partial data won't do). You can get sample JSON
from hledger-web's /transactions or /accounttransactions, or you
can export it with hledger-lib, eg like so:
.../hledger$ stack ghci hledger-lib
>>> writeJsonFile "txn.json" (head $ jtxns samplejournal)
>>> :q
Here's how it looks as of hledger-1.17 (remember, this JSON corresponds to hledger's Transaction and related data types):
{
"tcomment": "",
"tpostings": [
{
"pbalanceassertion": null,
"pstatus": "Unmarked",
"pamount": [
{
"aprice": null,
"acommodity": "$",
"aquantity": {
"floatingPoint": 1,
"decimalPlaces": 10,
"decimalMantissa": 10000000000
},
"aismultiplier": false,
"astyle": {
"ascommodityside": "L",
"asdigitgroups": null,
"ascommodityspaced": false,
"asprecision": 2,
"asdecimalpoint": "."
}
}
],
"ptransaction_": "1",
"paccount": "assets:bank:checking",
"pdate": null,
"ptype": "RegularPosting",
"pcomment": "",
"pdate2": null,
"ptags": [],
"poriginal": null
},
{
"pbalanceassertion": null,
"pstatus": "Unmarked",
"pamount": [
{
"aprice": null,
"acommodity": "$",
"aquantity": {
"floatingPoint": -1,
"decimalPlaces": 10,
"decimalMantissa": -10000000000
},
"aismultiplier": false,
"astyle": {
"ascommodityside": "L",
"asdigitgroups": null,
"ascommodityspaced": false,
"asprecision": 2,
"asdecimalpoint": "."
}
}
],
"ptransaction_": "1",
"paccount": "income:salary",
"pdate": null,
"ptype": "RegularPosting",
"pcomment": "",
"pdate2": null,
"ptags": [],
"poriginal": null
}
],
"ttags": [],
"tsourcepos": {
"tag": "JournalSourcePos",
"contents": [
"",
[
1,
1
]
]
},
"tdate": "2008-01-01",
"tcode": "",
"tindex": 1,
"tprecedingcomment": "",
"tdate2": null,
"tdescription": "income",
"tstatus": "Unmarked"
}
And here's how to test adding it with curl. This should add a new entry to your journal:
$ curl http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @txn.json
DEBUG OUTPUT
Debug output
You can add --debug[=N] to the command line to log debug output. N
ranges from 1 (least output, the default) to 9 (maximum output).
Typically you would start with 1 and increase until you are seeing
enough. Debug output goes to stderr, interleaved with the requests
logged on stdout. To capture debug output in a log file instead, you can
usually redirect stderr, eg:hledger-web --debug=3 2>hledger-web.log.
ENVIRONMENT
LEDGER_FILE The main journal file to use when not specified with
-f/--file. Default: $HOME/.hledger.journal.
BUGS
We welcome bug reports in the hledger issue tracker (https://bugs.hledger.org), or on the hledger chat or mail list (https://hledger.org/support).
Some known issues:
Does not work well on small screens, or in text-mode browsers.
2 minute quick start
Here is a sequence of examples, focussed on command-line usage:
$ brew install hledger # or apt, choco, but check Install for freshness
$ cat >main.journal # record a transaction manually from command line
2022-01-01 opening balances as of this date
assets:bank:checking $1000 ; This is a comment.
assets:bank:savings $2000 ; 2+ spaces are required before each amount.
assets:cash $100
liabilities:creditcard $-50
equity:opening/closing balances
^D
$ export LEDGER_FILE=main.journal # use this file by default
$ echo 'export LEDGER_FILE=main.journal' >>~/.profile # and in future sessions
$ hledger add # record a transaction interactively
Adding transactions to journal file main.journal
Any command line arguments will be used as defaults.
Use tab key to complete, readline keys to edit, enter to accept defaults.
An optional (CODE) may follow transaction dates.
An optional ; COMMENT may follow descriptions or amounts.
If you make a mistake, enter < at any prompt to go one step backward.
To end a transaction, enter . when prompted.
To quit, enter . at a date prompt or press control-d or control-c.
Date [2022-02-08]: 2/15
Description: market
Account 1: expenses:food
Amount 1: $50
Account 2: assets:cash
Amount 2 [$-50]:
Account 3 (or . or enter to finish this transaction):
2022-02-15 market
expenses:food $50
assets:cash $-50
Save this transaction to the journal ? [y]:
Saved.
Starting the next transaction (. or ctrl-D/ctrl-C to quit)
Date [2022-02-15]:
$ hledger stats # show journal statistics
Main file : main.journal
Included files :
Transactions span : 2022-01-01 to 2022-02-16 (46 days)
Last transaction : 2022-02-15 (7 days from now)
Transactions : 2 (0.0 per day)
Transactions last 30 days: 0 (0.0 per day)
Transactions last 7 days : 0 (0.0 per day)
Payees/descriptions : 1
Accounts : 6 (depth 3)
Commodities : 1 ($)
Market prices : 0 ()
Run time (throughput) : 0.04s (47 txns/s)
$ hledger bal --monthly # show account balance changes each month
Balance changes in 2022-01-01..2022-02-28:
|| Jan Feb
=================================++==============
assets:bank:checking || $1000 0
assets:bank:savings || $2000 0
assets:cash || $100 $-50
equity:opening/closing balances || $-3050 0
expenses:food || 0 $50
liabilities:creditcard || $-50 0
---------------------------------++--------------
|| 0 0
$ cat >checking.csv # make some CSV data, as if downloaded from a bank
"Date","Note","Amount"
"2022/2/01","GOODWORKS CORP","-1000.00"
"2022/2/22","PROPERTY MGMT CO","500.00"
"2022/2/23","ATM WITHDRAWAL","-100.00"
^D
$ cat >checking.csv.rules # and a rules file to help hledger read it
skip 1
fields date, description, amount
account1 assets:bank:checking
currency $
amount -%amount
if GOODWORKS
account2 income:salary
if PROPERTY
account2 expenses:rent
if ATM WITHDRAWAL
account2 assets:cash
^D
$ hledger import checking.csv # import CSV records as new journal entries
imported 2 new transactions from checking.csv
$ hledger import checking.csv # records already seen are ignored; cf --dry-run
no new transactions found in checking.csv
$ hledger print date:202202 # show transactions in february
2022-02-01 GOODWORKS CORP
assets:bank:checking $1000
income:salary $-1000
2022-02-15 market
expenses:food $50
assets:cash $-50
2022-02-22 PROPERTY MGMT CO
assets:bank:checking $-500
expenses:rent $500
2022-02-23 ATM WITHDRAWAL
assets:bank:checking $-100
assets:cash $100
$ hledger is -M # show a monthly income statement (profit & loss report)
Income Statement 2022-01-01..2022-02-28
|| Jan Feb
===============++============
Revenues ||
---------------++------------
income:salary || 0 $1000
---------------++------------
|| 0 $1000
===============++============
Expenses ||
---------------++------------
expenses:food || 0 $50
expenses:rent || 0 $500
---------------++------------
|| 0 $550
===============++============
Net: || 0 $450
$ hledger bs -M --tree # show monthly asset and liability balances
Balance Sheet 2022-01-31..2022-02-28
|| 2022-01-31 2022-02-28
========================++========================
Assets ||
------------------------++------------------------
assets || $3100 $3550
bank || $3000 $3400
checking || $1000 $1400
savings || $2000 $2000
cash || $100 $150
------------------------++------------------------
|| $3100 $3550
========================++========================
Liabilities ||
------------------------++------------------------
liabilities:creditcard || $50 $50
------------------------++------------------------
|| $50 $50
========================++========================
Net: || $3050 $3500
$ hledger areg checking # show checking's transactions and running balance
Transactions in assets:bank:checking and subaccounts:
2022-01-01 opening balances as:ba:savings, as.. $1000 $1000
2022-02-01 GOODWORKS CORP in:salary $1000 $2000
2022-02-22 PROPERTY MGMT CO ex:rent $-500 $1500
2022-02-23 ATM WITHDRAWAL as:cash $-100 $1400
$ hledger-ui --forecast # start the terminal UI (except on Windows)

$ hledger-ui --tree -f examples/bcexample.hledger # a multicurrency journal

$ hledger-web # start the web UI

$ hledger-web -f examples/bcexample.hledger # from data by Martin Blais

5 minute quick start
1. Record transactions
hledger reads transactions from a journal file -
usually ~/.hledger.journal, or C:\Users\USER\.hledger.journal, or whatever you’ve set LEDGER_FILE to.
Transactions are recorded like this:
2025-04-10 MyGas
Expenses:Automotive $20
Liabilities:Acme Credit Card
A date and description, followed by several indented account postings, with two or more spaces between each account name and its amount. Colons indicate subaccounts. Here are the parts in more detail:
A transaction is a movement of money between accounts. So the amounts in a transaction must add up to zero. If you leave one amount blank, it will be calculated automatically ($-20 in this case).
A positive amount means “added to this account”, a negative amount means “removed from this account” (debit and credit).
Here’s the start of a journal, with comments. To follow along with these examples, save this as your journal file. You can use a text editor; or you could run hledger add or hledger web and enter these transactions interactively (no need to enter the comments).
2023-01-01 opening balances ; <- First transaction sets starting balances.
assets:bank:checking $1000 ; <- Account names can be anything.
assets:bank:savings $2000 ; <- Colons indicate subaccounts.
assets:cash $100 ; <- 2+ spaces are required before the amount.
liabilities:credit card $-50 ; <- A debt; these are negative.
equity:opening/closing $-3050 ; <- Starting balances come from equity.
; Equity is also usually negative.
; (Reports can show as positive when needed.)
2023-02-01 GOODWORKS CORP ; <- Date order is recommended but optional.
assets:bank:checking $1000
income:salary ; <- $-1000 is inferred here to balance the txn.
; Income amounts are negative.
2023-02-15 market
expenses:food $50
assets:cash ; <- $-50 is inferred here.
Some people record all transactions by hand in this way. But you can also import them from bank data, as we’ll see below.
2. Add declarations
Transaction entries like the above are all you need to get started, so feel free to skip this step till later. But we usually add some declarations at the top of the file to enable more error checking.
First, declare your currencies/commodities, and their display style. In this case there’s only one, $:
commodity $1000.00
or it could be:
commodity $ 1.000,00
Now, you can check that all amounts have a valid commodity symbol:
$ hledger check commodities
$
Next, declare your top level accounts and their type, eg like this (or in other languages: ar da de en es fr ja ko no pt se zh …):
account assets ; type:A
account liabilities ; type:L
account equity ; type:E
account income ; type:R
account expenses ; type:X
account assets:bank ; type:C
account assets:cash ; type:C
account equity:conversion ; type:V
This helps reports show the right accounts. It also sets their preferred display order.
If you’d like more error checking, declare all account names, not just the top-level ones:
account assets ; type:A
account assets:bank ; type:C
account assets:bank:checking
account assets:bank:savings
account assets:cash ; type:C
account liabilities ; type:L
account liabilities:credit card
account equity ; type:E
account equity:conversion ; type:V
account equity:opening/closing
account income ; type:R
account income:salary
account income:gifts
account expenses ; type:X
account expenses:rent
account expenses:food
account expenses:gifts
Then you can check that all transactions use valid account names:
$ hledger check accounts
$
You can also use strict mode,
which enables both of these checks,
by adding -s to any command (or to your config file).
Here’s the above journal in full.
3. Run reports
Now you can see reports, such as…
A list of accounts, showing the hierarchy and types detected:
$ hledger accounts --tree --types
assets ; type: A
bank ; type: C
checking ; type: C
savings ; type: C
cash ; type: C
liabilities ; type: L
credit card ; type: L
equity ; type: E
conversion ; type: V
opening/closing ; type: E
income ; type: R
salary ; type: R
gifts ; type: R
expenses ; type: X
rent ; type: X
food ; type: X
gifts ; type: X
A balance sheet, showing what you own and owe:
$ hledger bs
Balance Sheet 2023-02-15
|| 2023-02-15
=========================++============
Assets ||
-------------------------++------------
assets:bank:checking || $2000
assets:bank:savings || $2000
assets:cash || $50
-------------------------++------------
|| $4050
=========================++============
Liabilities ||
-------------------------++------------
liabilities:credit card || $50
-------------------------++------------
|| $50
=========================++============
Net: || $4000
An income statement (AKA profit and loss report), showing what you received and spent:
$ hledger is -MTA
Income Statement 2023-01-01..2023-02-28
|| Jan Feb Total Average
===============++==============================
Revenues ||
---------------++------------------------------
income:salary || 0 $1000 $1000 $500
---------------++------------------------------
|| 0 $1000 $1000 $500
===============++==============================
Expenses ||
---------------++------------------------------
expenses:food || 0 $50 $50 $25
---------------++------------------------------
|| 0 $50 $50 $25
===============++==============================
Net: || 0 $950 $950 $475
An account register, showing the transactions and running balance in a particular account:
$ hledger aregister checking
Transactions in assets:bank:checking and subaccounts:
2023-01-01 opening balances as:ba:savings, as:.. $1000 $1000
2023-02-01 GOODWORKS CORP in:salary $1000 $2000
Congratulations, you can now begin to track your daily finances with hledger!
Docs has more detailed help.
10 minute quick start
What is it ?
hledger: free GPLv3+ accounting software for linux, mac, windows, web, etc.
- reads transactions from a flexible, future-proof, version-controllable plain text format
- or CSV files from any financial institution
- produces precise multiperiod financial reports as text/HTML/CSV/JSON/SQL
(balance sheet, income statement, cashflow, budget, roi, transactions, time, forecast…) - unlimited account hierarchy with summarising, aliasing, pivoting
- unlimited currencies/commodities, with cost/market valuation
- use via CLI, TUI, WUI, JSON API or Haskell library
- easy to script and extend
- user-friendly, well documented, robust
- scales smoothly from simple, easy accounting needs to complex ones.
How do I use it ?
At the start:
- Install one or more of the hledger tools
- Set up a journal, and maybe version control
On a regular basis (eg daily, can be <5m):
- Enter transactions manually and/or
- Import transactions from banks’ CSV
- Reconcile to catch mistakes
Whenever you like:
- Run reports to answer questions and gain insight
- Refine account names, CSV rules etc. to improve your reports and efficiency.
Knowing some double entry accounting will help you get the most from hledger, but you can do fine just by following the examples below. You’ll find your bookkeeping/accounting skills improve naturally (and help is available).
Install
Quickly: install hledger binaries, using your system package manager or our Github binaries.
Or slowly: build hledger from source, using hledger-install.sh or stack or cabal.
Set up a journal
The journal file is a plain text file where transactions are recorded. By default it is ~/.hledger.journal, and the add command or web add form described below will create it automatically, so actually you don’t need to do anything here.
But here are some common changes people make sooner or later, so why not now:
-
A dedicated folder, to consolidate financial files and make version control and backups easier:
$ mkdir ~/finance $ cd ~/finance -
A separate journal file for each year, for performance and data compartmentalisation:
$ touch 2023.journal -
A LEDGER_FILE environment variable, so you won’t have to type “-f ~/finance/2023.journal” with every command:
$ echo "export LEDGER_FILE=~/finance/2023.journal" >> ~/.bashrc $ source ~/.bashrcOr if environment variables annoy you, symbolic-link the file to ~/.hledger.journal:
$ ln -s ~/finance/2023.journal ~/.hledger.journal -
Some optional directives, useful especially with non-english account names:
$ cat > 2023.journal ; Declare the five top level accounts in your preferred language/capitalisation; ; their types (to make reports like bs and is work); and their display order. ; Write at least 2 spaces before the ; comments. account assets ; type:A, money you own. 2+ spaces are required before the ;. account liabilities ; type:L, money you owe to others account equity ; type:E, equal to A - L (not used much in personal finance) account revenues ; type:R, revenue/income categories account expenses ; type:X, expense categories ; Declare commodities/currencies and their decimal mark, digit grouping, ; number of decimal places.. commodity $1000.00 commodity 1.000,00 EUR <CTRL-D> (paste the command & text above into the terminal, then press control-d) -
Version control, for tracking changes:
$ git init $ git add 2023.journal $ git commit 2023.journal -m 'start 2023 journal' -
Remember to also keep backups.
Enter transactions
Recording transactions manually may sound tedious, but with a good text editor or other data entry tool it can be fast. It also provides greatest financial awareness. Some people enter everything by hand for this reason.
Run the add command for assisted data entry in the terminal (tutorial):
$ hledger add
...
Date [2023-03-10]: ...
Or run hledger-web and when the web browser opens, press a to add (tutorial):
$ hledger-web
...
Opening web browser...
Or using a text editor, add transactions to your journal file like so:
2023-01-01 opening balances on january 1st
assets:checking $1000 ; a posting, increasing assets:checking's balance by $1000
assets:cash $100 ; write at least 2 spaces between account name and amount
liabilities $0
equity $-1100 ; each transaction must sum to zero
2023-03-05 client payment
assets:checking $2000
revenues:consulting $-2000 ; revenues/liabilities/equity normally appear negative
2023-03-20 Sprouts
expenses:food:groceries $100
assets:cash $40
assets:checking ; a missing amount will be inferred ($-140 here)
As shown above, make the first transaction a dummy one that sets the opening balances of your asset & liability accounts on some start date. hledger will show accurate real-world account balances from this date onward, as long as you record the subsequent transactions.
To make things easy on yourself, you can pick a very recent start date, like today or last monday. Prioritise recording the transactions that happen after this date. (Tip: the more often you do this, the easier it is.)
Then, as your time and financial records and desire for historical reports allow, you can add older transactions. As you do, you’ll need to adjust the opening balances transaction, moving it back in time. Perhaps focus on one account at a time, each with its own opening balances transaction if necessary.
Import transactions
Import means 1. convert transaction data from some other format (usually a downloaded CSV file) and 2. save any new transactions to the main journal file. It is often possible to automate this, perhaps to the point of a nightly cron job and no manual data entry at all. This is convenient but costs some financial awareness.
Download one or more CSV files containing transaction info, then create a csv rules file for each. Eg if SomeBank.csv looks like:
"Date","Note","Amount"
"2023/3/22","DEPOSIT","50.00"
"2023/3/23","ATM WITHDRAWAL","-10.00"
Create SomeBank.csv.rules containing rules like:
skip 1
fields date, description, amount
currency $
account1 assets:checking
account2 expenses:misc
if DEPOSIT
account2 revenues:misc
if ATM WITHDRAWAL
account2 assets:cash
Check the csv conversion looks ok:
$ hledger -f SomeBank.csv print
2023-03-22 DEPOSIT
assets:checking $50.00
revenues:misc $-50.00
2023-03-23 ATM WITHDRAWAL
assets:checking $-10.00
assets:cash $10.00
You can run reports directly from the csv, but I like to import the new transactions into the main journal, keeping things in one place. The import command ignores csv records it has seen before, saving the latest dates in .latest.SomeBank.csv. This works for most csv files - you can try a dry run first:
$ hledger import *.csv --dry-run
; would import 2 new transactions from SomeBank.csv:
2023-03-22 DEPOSIT
assets:checking $50.00
revenues:misc $-50.00
2023-03-23 ATM WITHDRAWAL
assets:checking $-10.00
assets:cash $10.00
$ hledger import *.csv
imported 2 new transactions from SomeBank.csv
$ hledger import *.csv
no new transactions found in SomeBank.csv
Now to commit the new rules file and changed journal file:
$ git add SomeBank.csv.rules
$ git commit -m 'SomeBank csv rules' SomeBank.csv.rules
$ git commit -m 'txns' 2023.journal
In the above workflow, the journal file is permanent and downloaded csv files are temporary. Some folks (Full-fledged hledger, hledger-flow) prefer to instead commit all csv files and regenerate the journal file.
Reconcile
After entering or importing transactions, it’s important to check for mistakes (yours or others’), by comparing your reports with reality - your wallet, statements, online balances etc. See Reconciling.
Run reports
$ hledger accounts # account names declared and used, as a list
assets
assets:cash
assets:checking
liabilities
equity
revenues
revenues:consulting
expenses
expenses:food:groceries
$ hledger accounts --tree # accounts are actually a hierarchy
assets
cash
checking
equity
expenses
food
groceries
liabilities
revenues
consulting
$ hledger balancesheet # what do I own and owe ?
$ hledger bs # short form
Balance Sheet 2023-03-20
|| 2023-03-20
=================++============
Assets ||
-----------------++------------
assets:cash || $140
assets:checking || $2860
-----------------++------------
|| $3000
=================++============
Liabilities ||
-----------------++------------
-----------------++------------
||
=================++============
Net: || $3000
$ hledger aregister --forecast checking # or: hledger register checking
Transactions in assets:checking and subaccounts:
2023-01-01 opening balances .. as:cash, liabiliti.. $1000 $1000
2023-03-05 client payment re:consulting $2000 $3000
2023-03-20 Sprouts ex:fo:groceries, a.. $-140 $2860
$ hledger incomestatement --monthly --depth 2 # where is it coming from and going to ?
$ hledger is -M -2 # short form
Income Statement 2023Q1
|| Jan Feb Mar
=====================++=================
Revenues ||
---------------------++-----------------
revenues:consulting || 0 0 $2000
---------------------++-----------------
|| 0 0 $2000
=====================++=================
Expenses ||
---------------------++-----------------
expenses:food || 0 0 $100
---------------------++-----------------
|| 0 0 $100
=====================++=================
Net: || 0 0 $1900
$ hledger # show commands
$ hledger --help # show general options
$ hledger --man # show hledger's man page
$ hledger --info # show hledger's Info manual
$ hledger is --help # show incomestatement's options and docs
$ hledger is --man # show incomestatement in man page
$ hledger is --info # show incomestatement's Info page
$ hledger help # show hledger docs in best available viewer
$ hledger help incomestatement # show incomestatement docs in best available viewer
$ hledger-ui # start TUI
$ hledger-web # start WUI in default browser
For more detail, see:
-
hledger manual, web version
-
hledger.org website, including tutorials and cookbook docs
A tax reporting setup
Tax prep is done in a subdirectory of the year.:
finance/
2023/
taxes/
Health_Insurance_Form1095a_2023.pdf
(1099s, etc..)
next.journal -> ../../2024/2024.journal
prev.journal -> ../../2022/2022.journal
tax-reports.org.gpg
tax-reports.org (optionally encrypted with gpg) is the main thing;
it is an emacs org outline shaped like the tax forms or tax
preparation software (freetaxusa.com in my case).
For each form item/field, there’s an outline heading, and under it one or more hledger reports designed to show (and explain) the required information. Here’s an excerpt:
* tax preparation for 2023 -*- org-confirm-babel-evaluate:nil; -*-
...
** Income
*** overview
#+begin_src hledger :cmdline -f ../2???.journal -p 2023 bal -Y type:r cur:. -4 --invert
#+end_src
#+RESULTS:
#+begin_example
Balance changes in 2023:
...
#+end_example
*** Common Income
**** Interest Income
#+begin_src hledger :cmdline -f ../2???.journal -p 2023 bal -Y type:r interest --invert
#+end_src
#+RESULTS:
: Balance changes in 2023:
...
The reports are embedded as org source blocks. With cursor on the
command line, C-c C-c runs the command and updates the RESULTS.
(The two RESULTS above have different markup, because of output size -
(setq org-babel-min-lines-for-block-output ...) to control this.)
The commands reference ../2???.journal (2023.journal in this case),
and some of them use prev.journal or next.journal as well.
I used to define tax-specific account aliases renaming my usual account names to match the names used in tax forms/tax software, but that’s work and another layer of confusion, so currently I don’t bother with that.
I have used this setup for a number of years now and for me it gives a good combination of manageability, tweakability, repeatability, and persistence/memory across years.
For example, when starting tax prep for a year, I start with a copy of
the previous year’s outline, which includes all of last year’s reports.
As I press C-c C-c in each report, it updates to this year. This is
useful for comparison and sanity checking. Each year, the process
becomes easier.
Accounting basics for PTA users
Here we’ll give a quick hledger-oriented intro to some useful accounting concepts, using the journal file created in Tutorial: hledger add. Also we’ll discuss account hierarchy in hledger.
Debits and Credits
Double-entry bookkeeping traditionally names movements of money as “debits” or “credits”. As an error-checking mechanism, the debits must exactly balance the credits, both within each individual transaction and over all transactions.
Signed numbers
hledger and most other plain text accounting tools use positive and negative sign instead of the debit and credit labels. This is essentially the same system, but most people find it easier to learn than the debit/credit terminology. Positive numbers are debits, negative numbers are credits, and within each transaction (and over all transactions) the sum of amounts must be zero.
As a consequence in hledger and similar PTA tools, it’s common for reports to show equity, liability, and revenue (income) balances as negative numbers. Some hledger reports can show them as positive by default.
Here is Ledger’s discussion of this.
Assets, Liabilities and Equity
Accounting describes the status of a business, person or other entity at any point in time in terms of three amounts:
- Assets - Things owned
- Liabilities - Things owed
- Equity - The amount invested by owners/shareholders
The foundation of double-entry accounting is the accounting equation, which says Equity is always equal to Assets minus Liabilities (or, Net Assets).
This is also written as: Assets = Liabilities + Equity. Another way to say it: what the entity owns is funded either by debt or by the capital provided by its owners.
These three are called the Balance Sheet accounts. Their balances summarise the overall financial status at some point in time.
Revenue and Expenses
Two more amounts are used to describe changes in the above during a given period:
- Revenue - Money flowing in
- Expenses - Money flowing out
You may be accustomed to using the word Income instead of Revenue. That’s fine, just remember that Income is sometimes used to mean Net Income, which is Revenue - Expenses.
These two are called the Income Statement accounts. The balances they accumulate during some period of time indicate the inflows and outflows during that period (which will affect the Assets and Liabilities balances).
Chart of Accounts
Five numbers do not give a lot of detail. If you want to know what portion of expenses went to buy food, you could add up just the transactions with (say) “supermarket” in their description. You know how to do this with hledger:
$ hledger register desc:supermarket expenses
2015/05/25 trip to the super.. expenses $10 $10
But descriptions are irregular, so eg the report above misses the $5 purchase on the following day.
Instead, bookkeepers usually subdivide the top-level accounts into subaccounts, subsubaccounts, etc.
which can be used in transactions to record more specific categories.
This forms a hierarchy or tree of accounts, called the Chart of Accounts.
Here’s a simple example where assets, revenue and expenses each have a few subaccounts:
assets
checking
cash
liabilities
equity
revenue
business income
gifts received
expenses
food
rent
supplies
In some organisations and accounting systems (eg, QuickBooks), the tree structure is de-emphasised, so the above is represented more like:
Account name Account type
-------------------------------
checking ASSET
cash ASSET
business income REVENUE
gifts received REVENUE
food EXPENSE
rent EXPENSE
supplies EXPENSE
In others, the tree structure is encoded as decimal account numbers, something like this:
1000 assets
1100 checking
1200 cash
2000 liabilities
3000 equity
4000 revenue
4100 business income
4200 gifts received
5000 expenses
5100 food
5200 rent
5300 supplies
A digression: subaccounts in hledger
With hledger, tree structure is implied by writing account names like ACCOUNT:SUBACCOUNT.
Try it: edit your journal file and change the account names like so:
$ cat ~/.hledger.journal
2015/05/25 trip to the supermarket
expenses:supplies $10
assets:checking $-10
2015/05/26 forgot the bread
expenses:food $5
assets:cash
hledger will infer the chart of accounts from these names.
The accounts command will list all accounts posted to:
$ hledger accounts
assets:cash
assets:checking
expenses:food
expenses:supplies
and accounts --tree will show the tree structure, indenting subaccounts below their parents (and eliding the common part of their names):
$ hledger accounts --tree
assets
cash
checking
expenses
food
supplies
Similarly, the balance command shows a flat list of accounts and their balance changes, by default:
$ hledger balance
$-5 assets:cash
$-10 assets:checking
$5 expenses:food
$10 expenses:supplies
--------------------
0
And with --tree, it shows the account hierarchy:
$ hledger balance --tree
$-15 assets
$-5 cash
$-10 checking
$15 expenses
$5 food
$10 supplies
--------------------
0
In tree mode, the balance reported for parent accounts includes the balances of any subaccounts. Eg above, the $15 expenses balance is the sum of the subaccount balances ($5 expenses:food and $10 expenses:supplies).
hledger accepts whatever account names you choose, so you can use as much or as little account hierarchy as you need. Most users have at least two levels of accounts. You can limit the amount of detail in a balance report by hiding accounts below a certain depth:
$ hledger balance --depth 1
$-15 assets
$15 expenses
--------------------
0
Accounting: further study
Here are some useful links to learn more about bookkeeping, accounting and finance in general, beyond the Accounting basics for PTA users.
General
- Wikipedia: Accounting, Bookkeeping, Double-entry bookkeeping system, General journal etc.
- Accounting 101 (PDF, edo.ca)
- Accounting For Dragons why you should know accounting
- Cliffs Notes: Accounting Principles I, Accounting Principles II
- Bean Counter - tutorials, such as So, you want to learn Bookkeeping!. This has been recommended on the ledger list and on HN.
- Accounting Coach
- Accounting Basics
- Guru 99 Accounting Tutorials
- principlesofaccounting.com
- Double Entry Bookkeeping discussion by software developers at the WikiWikiWeb
- Winning Financially is Simple and other good episodes from the YNAB Podcast
- Back to the Stone Age: Low-Tech Expense Tracking Get Rich Slowly
- Track Every Penny You Spend Get Rich Slowly
- I’ve Tracked My Expenses — Now What? Get Rich Slowly
- A Slow-Tech Approach to Tracking Spending
- Your Financial Network Map
- The Accountancy Model and The Accountancy Model Examples - two free books by Tim Riley
- Gnucash and double entry accounting - double entry accounting introduction with examples
- Accounting for Computer Scientists
- Tutorial on multiple currency accounting by Peter Selinger
- Financial Statements: A Beginner’s Guide
- Finances 2: Guide nice PTA-applicable intro from this mac/iphone app. Recommended.
- Solarpunk
Video
- Accounting course by Prof. Krug 2011
- Khan Academy: Accounting and financial statements
- Tea Leaves’ Double-Entry Bookkeeping and Counting Like It’s 1479 playlists. Warning, these are not in good didactic order, so pick and choose. Eg :
Theory
- Algebraic Models for Accounting Systems - recommended on twitter
- On Double-Entry Bookkeeping: The Mathematical Treatment David Ellerman 2014, http://www.ellerman.org/double-entry-bookkeeping
- Momentum accounting and triple-entry bookkeeping
- Essence of Accounting “A logical description of the record to report process: accounting, reporting, auditing, analysis”, Charles Hoffman 2020
History
- History of Accounting, Wikipedia
- From accounting to negative numbers: A signal contribution of medieval India to mathematics, Accounting Historians Journal 1998
- The Vanished Grandeur of Accounting & discussion, Boston Globe
- How the world’s first accountants counted on cuneiform, BBC News
- The Significance of Ancient Mesopotamia in Accounting History
- Mesopotamian Tablet Collection, Spurlock Museum
- Ledger Art
- Etymological observations on some accounting terms
Balancing the accounting equation
The Accounting Equation
states that Assets and Liabilities always match Equity. Eg: A - L = E.
This suggests that a balance report showing all Asset,
Liability and Equity account balances should show a zero grand
total. With hledger you can check this with a balance report like:
$ hledger balance ^assets ^liabilities ^equity
or more easily with the balancesheetequity command, which is designed for this:
$ hledger bse
Note, checking the accounting equation is different from checking a
trial balance.
A trial balance just checks that the total inflows and outflows over all accounts are equal,
which can be seen by a zero grand total for hledger balance.
Normally this is ensured by hledger’s requirement that each individual transaction is balanced,
but some of the same problems noted below apply to this also.
Common problems
In practice, you will find quite a number of things in real-life journals can disrupt the accounting equation and cause a non-zero total. Note, this does not interfere with most day-to-day reporting, and many PTA users won’t notice it as a problem. But, seeing the correct zero total gives added confidence in your bookkeeping, for yourself and others you might be sharing reports with.
Here are some things that disturb the accounting equation, and their solutions:
1. Unclosed revenue/expenses
Revenues (income) and expenses are technically part of
equity. In traditional accounting, they should be transferred to an account like
equity:retained earnings at the end of each reporting period.
You could record such transfers in your journal, either manually or using close –retain. Most PTA users don’t bother with this.
More conveniently, you can use an account alias to convert revenue/expense accounts to equity temporarily.
Eg:--alias '/^(revenues|income|expenses)\b/=equity'
However, that doesn’t work if you have declared your account types with type: tags.
Alternatively, you could try including close --retain’s output on the fly (which is probably what the bse command should have built in).
Eg if you are using bash:
hledger -f $LEDGER_FILE -f <(hledger close --retain) bse
2. Unbalanced conversions with cost notation (@/@@)
Currency/commodity conversions using @/@@ notation and no equity conversion postings are unbalanced. You can rewrite them in balanced form, eg combining costs and equity conversion postings.
Or, use --infer-equity to do this temporarily at report time.
Or, converting amounts to cost may be another solution - try adding -B.
3. Rounding error with unit costs (@) and –infer-equity
--infer-equity is convenient but it tends to expose inaccuracies in
the recorded @ prices, causing small non-zero values in the total. You
can ignore this, or try to fix it by making @ prices more accurate, or
replace your uses of @ with @@ (?) or equity postings.
4. Posting dates
Postings dates different from their transaction’s date (; date:DATE
or ; [DATE] notation) cause an imbalance in the accounting equation
between the transaction date and posting date. Usually these unbalanced
periods are short and do not cross a file boundary, so you can just
avoid them when testing the accounting equation.
If they do cross a file boundary, or are inconveniently long, you can fix that by splitting the transaction into two transactions which use a pending account, as in close and balance assertions.
5. Unbalanced postings
Unbalanced virtual postings (with parenthesised account names)
create an imbalance by definition; just exclude them from the report with
-R/--real. This also excludes balanced virtual postings (with
bracketed account names), but that will probably be harmless.
6. Partial reports
Many kinds of report query exclude some data, which can disturb the accounting equation.
So, it’s best to avoid queries when checking the accounting equation.
If you specify a report start date, be sure to include
balances from previous transactions by adding -H/--historical.
(Or use the bse command, which does this automatically.)
7. Disrupted balance assertions
Some combination of the above may cause balance assertions to fail, in which case you can disable those with -I.
An improved accounting equation report
So combining these workarounds, here are some commands more likely to accurately check the accounting equation, for the moment. A zero final total indicates the accounting equation is balanced.
Merging RX with aliases
$ hledger bse -R --infer-equity --alias '/^(revenues|income|expenses)\b/=equity' not:desc:'closing balances' --layout tall
-R- (–real) excludes any unbalanced virtual postings--infer-equity- adds equity postings where needed to balance transactions using @/@@ cost notation--alias ...- recategorises all revenues/expenses as equitynot:desc:...- excludes any final closing balance transactions that would hide ending balances (suitable for checking a single journal file)--layout tall- improves readability when there are many commodities-f ...- optional, specifies a file other than the default $LEDGER_FILE.
Merging RX with close
$ hledger bse -R --infer-equity -f $LEDGER_FILE -f <(hledger close --retain) -I not:desc:'closing balances' --layout tall
<(...)- bash syntax, puts the command’s output in a temporary filehledger close --retain- generates transactions transferring revenues & expenses to equity-I- ignores any failing balance assertions
Trial balance report
You could also just use the balance command to show a full trial balance report involving all accounts, which may be simpler.
$ hledger bal type:ALERX -R --infer-equity not:desc:'closing balances' --layout tall
type:ALERX- consider only asset/liability/equity/revenue/expense accounts, not any non-standard accounts you might be using
Borrowing and Lending
Lending, calculating interest manually
0.41% interest per month (roughly equivalent to 5% APR), calculated manually:
2020-01-01 opening balances
assets:bank:checking 1000
equity:opening/closing balances
2020-01-01 lend to Trusty Tara
assets:bank:checking
assets:receivable:tt 100
2020-02-01 charge 5% interest
assets:receivable:tt 0.41 ; 100 x 0.41
revenues:interest:tt
2020-02-15 Tara payment
assets:receivable:tt -50
assets:bank:checking
2020-03-01 charge 5% interest
assets:receivable:tt 0.21 ; 50.41 x 0.41, rounded
revenues:interest:tt
2020-03-15 Tara payment
assets:receivable:tt -50
assets:bank:checking
Monthly balance sheet:
$ hledger bs -M
Balance Sheet 2020-01-31,,2020-03-31
|| 2020-01-31 2020-02-29 2020-03-31
======================++====================================
Assets ||
----------------------++------------------------------------
assets:bank:checking || 900.00 950.00 1000.00
assets:receivable:tt || 100.00 50.41 0.62
----------------------++------------------------------------
|| 1000.00 1000.41 1000.62
======================++====================================
Liabilities ||
----------------------++------------------------------------
----------------------++------------------------------------
||
======================++====================================
Net: || 1000.00 1000.41 1000.62
Lending, calculating interest with hledger-interest
Loan and payment transactions are in the main journal:
2020-01-01 opening balances
assets:bank:checking 1000.00
equity:opening/closing balances
2020-01-01 lend to Trusty Tara
assets:bank:checking
assets:receivable:tt 100 = 100
2020-02-15 Tara payment
assets:receivable:tt -50
assets:bank:checking
2020-03-15 Tara payment
assets:receivable:tt -50
assets:bank:checking
We use hledger-interest to add interest transactions, here 5% per year:
$ hledger-interest assets:receivable:tt --act --annual=0.05 -s revenues:interest:tt -t assets:receivable:tt
2020-01-01 lend to Trusty Tara
assets:bank:checking -100.00
assets:receivable:tt 100.00 = 100.00
2020-02-15 5% interest for 100.00 over 46 days
assets:receivable:tt 0.63
revenues:interest:tt -0.63
2020-02-15 Tara payment
assets:receivable:tt -50.00
assets:bank:checking 50.00
2020-03-15 5% interest for 50.63 over 29 days
assets:receivable:tt 0.20
revenues:interest:tt -0.20
2020-03-15 Tara payment
assets:receivable:tt -50.00
assets:bank:checking 50.00
It doesn’t print the opening balance transaction for some reason. So we’ll print that too, then get a monthly balance sheet:
$ (hledger print desc:opening; hledger-interest assets:receivable:tt --act --annual=0.05 -s revenues:interest:tt -t assets:receivable:tt) | hledger -f- bs -M
Balance Sheet 2020-01-31,,2020-03-31
|| 2020-01-31 2020-02-29 2020-03-31
======================++====================================
Assets ||
----------------------++------------------------------------
assets:bank:checking || 900.00 950.00 1000.00
assets:receivable:tt || 100.00 50.63 0.83
----------------------++------------------------------------
|| 1000.00 1000.63 1000.83
======================++====================================
Liabilities ||
----------------------++------------------------------------
----------------------++------------------------------------
||
======================++====================================
Net: || 1000.00 1000.63 1000.83
Budgeting and forecasting (2018)
This is an old but still useful intro to budgeting with hledger. For more about budgeting, see also:
Budgeting and forecasting allows you to keep better track of your expenses and future financial situation. If you write down your expectations of what your income/expenses/investment yields/etc should be, you can use them to:
- check how far off are your expectations from reality (budgeting)
- project your future account activity or balances (forecasting)
(This section uses examples/bcexample.hledger from hledger source repository).
Goal-based budgeting
To start budgeting, you need to know what your average yearly or weekly expenditures are. Hledger could help you with that. Usually the interval for which you compute budget figures will be the same as the interval between your paychecks – monthly or weekly.
Lets create monthly (-M) report for years 2013-2014 (-b 2013) of all top-level expense categories (–depth 2 Expenses), looking for average figures (-A), limiting ourselves to USD transactions only, to save screen space:
$ hledger balance -f bcexample.hledger -MA -b 2013 --depth 2 Expenses cur:USD
Balance changes in 2013/01/01-2014/10/31:
|| 2013/01 2013/02 2013/03 ... 2014/07 2014/08 2014/09 2014/10 Average
====================++========================================...==================================================================
Expenses:Financial || 4.00 USD 12.95 USD 39.80 USD ... 30.85 USD 21.90 USD 12.95 USD 4.00 USD 17.83 USD
Expenses:Food || 396.46 USD 481.48 USD 603.32 USD ... 871.20 USD 768.23 USD 466.72 USD 83.00 USD 562.10 USD
Expenses:Health || 290.70 USD 193.80 USD 193.80 USD ... 290.70 USD 193.80 USD 193.80 USD 96.90 USD 207.01 USD
Expenses:Home || 2544.98 USD 2545.02 USD 2544.97 USD ... 2545.12 USD 2545.01 USD 2545.10 USD 0 2429.33 USD
Expenses:Taxes || 5976.60 USD 3984.40 USD 4901.83 USD ... 5976.60 USD 3984.40 USD 3984.40 USD 1992.20 USD 4322.27 USD
Expenses:Transport || 120.00 USD 120.00 USD 120.00 USD ... 0 120.00 USD 120.00 USD 120.00 USD 109.09 USD
--------------------++----------------------------------------...------------------------------------------------------------------
|| 9332.74 USD 7337.65 USD 8403.72 USD ... 9714.47 USD 7633.34 USD 7322.97 USD 2296.10 USD 7647.64 USD
This report is rather wide and portion of it had been cut out for brevity. Most interesting column is the last one, it shows average monthly expenses for each category. Expenses in Food, Health, Home and Transport categories seem to roughly similar month to month, so lets create a budget for them.
Budgets are described with periodic (ie, recurring) transaction rules. Periodic transaction
has ~ instead of date and period expression instead of description. In this case
we want to create a monthly budget that will come into effect starting from January 2013,
which will include income of 10000 USD that is partially spent on Food, Health, Home and Transport
and the rest becomes our Assets:
~ monthly from 2013/01
Expenses:Food 500 USD
Expenses:Health 200 USD
Expenses:Home 2545 USD
Expenses:Transport 120 USD
Income:US -10700 USD ;; Taken as monthy average of Income account group
Assets:US
This transaction could be put into separate file (budget.journal) or could be kept in the main journal. Normally hledger will ignore it and will not include it in any computations or reports.
To put it into action, you need to add --budget switch to your balance invocation. If you do that,
you would be able to see how your past expenses aligned with the budget that you just created. This
time, lets not limit accounts in any way:
$ hledger balance -f bcexample.hledger -f budget.journal -MB -b 2013 --budget cur:USD
Balance changes in 2013/01/01-2014/10/31:
|| 2013/01 2013/02 2013/03
==========================++===========================================================================================================
<unbudgeted>:Expenses || 5980.60 USD 3997.35 USD 4941.63 USD
<unbudgeted>:Liabilities || 293.09 USD -147.51 USD -66.01 USD
Assets:US || 1893.32 USD [26% of 7335 USD] 2929.77 USD [40% of 7335 USD] -3898.89 USD [-53% of 7335 USD]
Expenses:Food || 396.46 USD [79% of 500 USD] 481.48 USD [96% of 500 USD] 603.32 USD [121% of 500 USD]
Expenses:Health || 290.70 USD [145% of 200 USD] 193.80 USD [97% of 200 USD] 193.80 USD [97% of 200 USD]
Expenses:Home || 2544.98 USD [100% of 2545 USD] 2545.02 USD [100% of 2545 USD] 2544.97 USD [100% of 2545 USD]
Expenses:Transport || 120.00 USD [100% of 120 USD] 120.00 USD [100% of 120 USD] 120.00 USD [100% of 120 USD]
Income:US || -15119.10 USD [141% of -10700 USD] -10331.21 USD [97% of -10700 USD] -11079.40 USD [104% of -10700 USD]
--------------------------++-----------------------------------------------------------------------------------------------------------
|| -3599.95 USD -211.30 USD -6640.58 USD
Numbers in square brackets give you your budget estimate and percentage of it used by your real expenses. Numbers below 100% mean that you have some of your budget left, numbers over 100% mean that you went over your budget.
You can notice that actual numbers for Assets:US seem to be well below computed budget of 7335 USD. Why? Answer to this is in the first row of the report: we have quite a lot of unbudgeted Expenses!
Notice that even though we have not limited accounts in any way, report includes just those mentioned in the budget. This is on purpose,
assumption is that when you are checking your budgets you probably do not want unbudgeted accounts getting in your way. Another thing to
note is that budget numbers have been allocated to top-level expense subcategories (like Expenses:Food). Journal has subaccounts under
Food, but to compute budget report they have all been rolled up into a nearest parent with budget number associated with it. Accounts that
do not have such parent went into <unbudgeted> row.
Allright, it seems that for Jan 2013 we have ~3000 USD of budgeted expenses and almost twice as much unbudgeted. Lets figure out what they are.
We can see more details if we add -E/--empty switch:
$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2013-01 -e 2013-02 --budget cur:USD -E
Balance changes in 2013/01:
|| 2013/01
==================================++====================================
Assets:US || 1893.32 USD [26% of 7335 USD]
Expenses:Financial:Fees || 4.00 USD
Expenses:Food || 396.46 USD [79% of 500 USD]
Expenses:Health || 290.70 USD [145% of 200 USD]
Expenses:Home || 2544.98 USD [100% of 2545 USD]
Expenses:Taxes:Y2013:US:CityNYC || 524.76 USD
Expenses:Taxes:Y2013:US:Federal || 3188.76 USD
Expenses:Taxes:Y2013:US:Medicare || 319.86 USD
Expenses:Taxes:Y2013:US:SDI || 3.36 USD
Expenses:Taxes:Y2013:US:SocSec || 844.62 USD
Expenses:Taxes:Y2013:US:State || 1095.24 USD
Expenses:Transport || 120.00 USD [100% of 120 USD]
Income:US || -15119.10 USD [141% of -10700 USD]
Liabilities:US:Chase:Slate || 293.09 USD
----------------------------------++------------------------------------
|| -3599.95 USD
All the accounts that were rolled up into <unbudgeted> category are now shown with their original name, but budgeted accounts are still rolled up. It
is easy to see now that we forgot taxes. Lets add them to our budget:
~ monthly from 2013/01
Expenses:Food 500 USD
Expenses:Health 200 USD
Expenses:Home 2545 USD
Expenses:Transport 120 USD
Expenses:Taxes 4300 USD ;; Taken from monthly average report
Income:US -10700 USD
Assets:US
Lets try again for a couple of month with this updated budget:
$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2013-01 -e 2013-04 --budget cur:USD
Balance changes in 2013q1:
|| 2013/01 2013/02 2013/03
==========================++===========================================================================================================
<unbudgeted>:Expenses || 4.00 USD 12.95 USD 39.80 USD
<unbudgeted>:Liabilities || 293.09 USD -147.51 USD -66.01 USD
Assets:US || 1893.32 USD [62% of 3035 USD] 2929.77 USD [97% of 3035 USD] -3898.89 USD [-128% of 3035 USD]
Expenses:Food || 396.46 USD [79% of 500 USD] 481.48 USD [96% of 500 USD] 603.32 USD [121% of 500 USD]
Expenses:Health || 290.70 USD [145% of 200 USD] 193.80 USD [97% of 200 USD] 193.80 USD [97% of 200 USD]
Expenses:Home || 2544.98 USD [100% of 2545 USD] 2545.02 USD [100% of 2545 USD] 2544.97 USD [100% of 2545 USD]
Expenses:Taxes || 5976.60 USD [139% of 4300 USD] 3984.40 USD [93% of 4300 USD] 4901.83 USD [114% of 4300 USD]
Expenses:Transport || 120.00 USD [100% of 120 USD] 120.00 USD [100% of 120 USD] 120.00 USD [100% of 120 USD]
Income:US || -15119.10 USD [141% of -10700 USD] -10331.21 USD [97% of -10700 USD] -11079.40 USD [104% of -10700 USD]
--------------------------++-----------------------------------------------------------------------------------------------------------
|| -3599.95 USD -211.30 USD -6640.58 USD
Now unbudgeted amounts are much smaller and some of them could be dismissed as noise, and we can see that budget created is actually close enough to the real numbers, meaning that they are usually close to average that we put in our budget.
Budget report that we have used so far assumes that any unused budget amount for a given (monthly) period will not contribute to the budget of the next period. Alternative popular “envelope budget” strategy assumes that you put a certain amount of money into an envelope each month, and any unused amount stays there for future expenses. This is easy to simulate by adding –cumulative switch. Lets redo the last report with it:
$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2013-01 -e 2013-04 --cumulative --budget cur:USD
Ending balances (cumulative) in 2013q1:
|| 2013/01/31 2013/02/28 2013/03/31
==========================++============================================================================================================
<unbudgeted>:Expenses || 4.00 USD 16.95 USD 56.75 USD
<unbudgeted>:Liabilities || 293.09 USD 145.58 USD 79.57 USD
Assets:US || 1893.32 USD [62% of 3035 USD] 4823.09 USD [79% of 6070 USD] 924.20 USD [10% of 9105 USD]
Expenses:Food || 396.46 USD [79% of 500 USD] 877.94 USD [88% of 1000 USD] 1481.26 USD [99% of 1500 USD]
Expenses:Health || 290.70 USD [145% of 200 USD] 484.50 USD [121% of 400 USD] 678.30 USD [113% of 600 USD]
Expenses:Home || 2544.98 USD [100% of 2545 USD] 5090.00 USD [100% of 5090 USD] 7634.97 USD [100% of 7635 USD]
Expenses:Taxes || 5976.60 USD [139% of 4300 USD] 9961.00 USD [116% of 8600 USD] 14862.83 USD [115% of 12900 USD]
Expenses:Transport || 120.00 USD [100% of 120 USD] 240.00 USD [100% of 240 USD] 360.00 USD [100% of 360 USD]
Income:US || -15119.10 USD [141% of -10700 USD] -25450.31 USD [119% of -21400 USD] -36529.71 USD [114% of -32100 USD]
--------------------------++------------------------------------------------------------------------------------------------------------
|| -3599.95 USD -3811.25 USD -10451.83 USD
If you look at Expenses:Food category, you will see that every month budget is increased by 500 USD, and by March total amount budgeted is 1500 USD, of which 1481.26 USD is spent. If you look back at the previous non-cumulative monthly budget report, you will see that in March food expenses were 121% of the budgeted amount, but cumulative report shows that taking into account budget carry-over from Jan and Feb we are well within planned numbers.
Envelope budgeting
Real envelope budgeting involves actually setting aside money for each category and spending only from there. In physical envelope budgeting, there are actual envelopes of cash. When doing envelope budgeting with hledger, the envelopes are represented by subaccounts, into which you transfer money and from which you spend it.
Note with this style of budgeting, you don’t use periodic transactions or the --budget report -
just regular transfers and regular balance reports.
At the end of each period you can decide to remove or reallocate any surpluses, or let them roll over.
A good place to keep the envelope accounts is under your regular checking or cash account,
since it keeps the overall balance correct.
Eg assets:checking:rent, assets:checking:food etc.
Some people use virtual (imaginary) accounts instead.
The advantage of envelope budgeting is that it models your available funds precisely, which can be important in tight cashflow situations. hledger doesn’t prevent overspending from your budget envelopes - you should watch their balances and make sure they never go negative. (The hledger-check-fancyassertions script could help, https://hledger.org/scripts.html.)
The downside is that it requires more bookkeeping work from you. Some people use auto posting rules to try to reduce that, but this can add complexity.
For more about envelope budgeting, see the links at plaintextaccounting.org/budgeting.
Forecasting
Budget transaction that was created could be used to predict what would be our financial situation in the future. If you add --forecast switch, you will
see how budgeted income and expense affects you past the last transaction in the journal. Since journal ends in Oct 2014, lets see next two month:
$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2014-10 -e 2015 --forecast cur:USD
Balance changes in 2014q4:
|| 2014/10 2014/11 2014/12
====================================++======================================
Assets:US || 0 3035 USD 3035 USD
Assets:US:BofA:Checking || -2453.40 USD 0 0
Assets:US:ETrade:Cash || 5000.00 USD 0 0
Expenses:Financial:Fees || 4.00 USD 0 0
Expenses:Food || 0 500 USD 500 USD
Expenses:Food:Restaurant || 83.00 USD 0 0
Expenses:Health || 0 200 USD 200 USD
Expenses:Health:Dental:Insurance || 2.90 USD 0 0
Expenses:Health:Life:GroupTermLife || 24.32 USD 0 0
Expenses:Health:Medical:Insurance || 27.38 USD 0 0
Expenses:Health:Vision:Insurance || 42.30 USD 0 0
Expenses:Home || 0 2545 USD 2545 USD
Expenses:Taxes || 0 4300 USD 4300 USD
Expenses:Taxes:Y2014:US:CityNYC || 174.92 USD 0 0
Expenses:Taxes:Y2014:US:Federal || 1062.92 USD 0 0
Expenses:Taxes:Y2014:US:Medicare || 106.62 USD 0 0
Expenses:Taxes:Y2014:US:SDI || 1.12 USD 0 0
Expenses:Taxes:Y2014:US:SocSec || 281.54 USD 0 0
Expenses:Taxes:Y2014:US:State || 365.08 USD 0 0
Expenses:Transport || 0 120 USD 120 USD
Expenses:Transport:Tram || 120.00 USD 0 0
Income:US || 0 -10700 USD -10700 USD
Income:US:Hoogle:GroupTermLife || -24.32 USD 0 0
Income:US:Hoogle:Salary || -4615.38 USD 0 0
Liabilities:US:Chase:Slate || -203.00 USD 0 0
------------------------------------++--------------------------------------
|| 0 0 0
Note that this time there is no roll-up of accounts. Unlike --budget, which could be used with balance command only, --forecast
could be used with any report. Forecast transactions would be added to your real journal and would appear in the report you requested as
if you have entered them on the scheduled dates.
Since quite a lot of accounts do not have any budgeted transactions, lets limit the depth of the report to avoid seeing lots of zeroes:
$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2014-10 -e 2015 --forecast cur:USD --depth 2
Balance changes in 2014q4:
|| 2014/10 2014/11 2014/12
====================++======================================
Assets:US || 2546.60 USD 3035 USD 3035 USD
Expenses:Financial || 4.00 USD 0 0
Expenses:Food || 83.00 USD 500 USD 500 USD
Expenses:Health || 96.90 USD 200 USD 200 USD
Expenses:Home || 0 2545 USD 2545 USD
Expenses:Taxes || 1992.20 USD 4300 USD 4300 USD
Expenses:Transport || 120.00 USD 120 USD 120 USD
Income:US || -4639.70 USD -10700 USD -10700 USD
Liabilities:US || -203.00 USD 0 0
--------------------++--------------------------------------
|| 0 0 0
As you can see, we should expect 3035 USD to be added into Assets:US each month. It is quite easy to see how overal amount of Assets will change with time if you use
--cumulative switch:
$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2014-10 -e 2015 --forecast cur:USD --depth 2 --cumulative
Ending balances (cumulative) in 2014q4:
|| 2014/10/31 2014/11/30 2014/12/31
====================++============================================
Assets:US || 2546.60 USD 5581.60 USD 8616.60 USD
Expenses:Financial || 4.00 USD 4.00 USD 4.00 USD
Expenses:Food || 83.00 USD 583.00 USD 1083.00 USD
Expenses:Health || 96.90 USD 296.90 USD 496.90 USD
Expenses:Home || 0 2545 USD 5090 USD
Expenses:Taxes || 1992.20 USD 6292.20 USD 10592.20 USD
Expenses:Transport || 120.00 USD 240.00 USD 360.00 USD
Income:US || -4639.70 USD -15339.70 USD -26039.70 USD
Liabilities:US || -203.00 USD -203.00 USD -203.00 USD
--------------------++--------------------------------------------
|| 0 0 0
According to forecast, assets are expected to grow to 8600+ USD by the end of 2014. However, our forecast does not include a couple of big one-off year end expenses. First, we plan to buy prize turkey for the Christmas table every year from 2014, spending up to 500 USD on it. And on 17th Nov 2014 we would celebrate birthday of significant other, spending up to 6000 USD in a fancy restaurant:
~ every 20th Dec from 2014
Expenses:Food 500 USD ; Prize turkey, the biggest of the big
Assets:US
~ 2014/11/17
Assets:US
Expenses:Food 6000 USD ; Birthday, lots of guests
Note that turkey transaction is not entered as “yearly from 2014/12/20”, since yearly/quarterly/monthy/weekly periodic expressions always generate entries at the first day of the calendar year/quarter/month/week. Thus “monthly from 2014/12” will occur on 2014/12/01, 2015/01/01, …, whereas “every 20th of month from 2014/12” will happen on 2014/12/20, 2015/12/20, etc.
With latest additions forecast now looks like this:
hledger balance -f bcexample.hledger -f budget.journal -M -b 2014-10 -e 2015 --forecast cur:USD --depth 2 --cumulative
Ending balances (cumulative) in 2014q4:
|| 2014/10/31 2014/11/30 2014/12/31
====================++============================================
Assets:US || 2546.60 USD -418.40 USD 2116.60 USD
Expenses:Financial || 4.00 USD 4.00 USD 4.00 USD
Expenses:Food || 83.00 USD 6583.00 USD 7583.00 USD
Expenses:Health || 96.90 USD 296.90 USD 496.90 USD
Expenses:Home || 0 2545 USD 5090 USD
Expenses:Taxes || 1992.20 USD 6292.20 USD 10592.20 USD
Expenses:Transport || 120.00 USD 240.00 USD 360.00 USD
Income:US || -4639.70 USD -15339.70 USD -26039.70 USD
Liabilities:US || -203.00 USD -203.00 USD -203.00 USD
--------------------++--------------------------------------------
|| 0 0 0
It is easy to see that in Nov 2014 we will run out of Assets. Using register we can figure out when or why it would happen:
$ hledger register -f bcexample.hledger -f budget.journal -b 2014-10 -e 2014-12 --forecast cur:USD Assets
2014/10/04 "BANK FEES" | "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD -4.00 USD
2014/10/09 "Hoogle" | "Payroll" Assets:US:BofA:Checking 2550.60 USD 2546.60 USD
2014/10/10 "Transfering accumulated savings to o.. Assets:US:BofA:Checking -5000.00 USD -2453.40 USD
Assets:US:ETrade:Cash 5000.00 USD 2546.60 USD
2014/11/01 Forecast transaction Assets:US 3035 USD 5581.60 USD
2014/11/17 Forecast transaction Assets:US -6000 USD -418.40 USD
It is 6000 USD planned for birthday! Something will have to be done about the birthday plans.
Budgeting
All budgeting-related docs
-
hledger > balance > Budget report, hledger’s built-in periodic budget report
-
Budgeting and forecasting (2018), a older introduction
-
https://github.com/simonmichael/hledger/tree/master/examples/budgeting, annotated examples of journals implementing budgets
-
plaintextaccounting.org: Budgeting, more budgeting-related docs, for all PTA tools
-
Age Your Money, the YNAB (YouNeedABudget) way
-
https://github.com/Kickball/awesome-selfhosted#money-budgeting--management, some other budgeting software for comparison
-
Time planning -> How to set up a time budget
Notes
<sm> two commands that are roughly equivalent: ledger budget --add-budget expenses, hledger balance --budget -E expenses
<sm> they show both budgeted and unbudgeted accounts
--budget has no effect on single-column reports, it requires a reporting interval
--budget INTERVAL enables all periodic transactions with that interval; these can be date-limited
--budget hides all non-budgeted subaccounts; can be depth-limited more
<sm> there's different ways to do budgeting [16:46]
<sm> let me try to count them [16:50]
<sm> "envelope budgeting" is analogous to having a set of envelopes containing cash for different purposes.
You can model the "envelopes" with
a. real-world accounts (eg your bank lets you create arbitrary savings accounts),
b. virtual (imaginary) subaccounts of a real-world account (eg your checking account),
c. virtual accounts "off to the side" (budget:*)
<sm> also you can do the transfers to and from these manually, or generate them with automated posting rules
<sm> "goal budgeting" (best name I can come up with) involves setting some inflow/outflow goals per account per period,
and then measuring how the actual flows compare with the goals. balance --budget provides this report
<sm> I think that's 7 ways
From https://www.reddit.com/r/plaintextaccounting/comments/doq9p5/new_to_ledger_budgeting_question:
Also search for budgeting links at http://plaintextaccounting.org . You’ll see two main approaches discussed:
“envelope budgeting” - sounds more like what you’ve been doing. Based around explicitly allocating money for each purpose. Good for managing > cashflow. Requires more journal entries. Can be done entirely manually (1a) but many docs advise using automatic posting rules to assist (1b). Many > different ways to handle the details. Requires more thinking.
the other kind (“report-based budgeting” ?). Based around a special budget report provided by Ledger/hledger, which uses periodic transaction > rules to set budget goals. Automatic posting rules might be useful here too, I’m not sure. Provides less enforcement, requires less work. Fewer ways > to do it, perhaps provides simpler/clearer reports.
I often find “budgeting” covers/touches on quite a lot of topics:
- setting earning/spending goals,
- reviewing performance against those goals,
- controlling earning/spending based on the goals,
- allocating funds for short term expenses,
- allocating funds towards savings goals,
- updating allocated funds as transactions occur,
- reallocating funds/balancing the budget,
- end of period actions (roll over ? reset ?),
- forecasting cash balances and managing cashflow,
- forecasting income/expenses…
–budget and subaccounts
You can add budgets to any account in your account hierarchy. If you have budgets on both parent account and some of its children, then budget(s) of the child account(s) would be added to the budget of their parent, much like account balances behave.
In the most simple case this means that once you add a budget to any account, all its parents would have budget as well.
To illustrate this, consider the following budget:
~ monthly from 2019/01
expenses:personal $1,000.00
expenses:personal:electronics $100.00
liabilities
With this, monthly budget for electronics is defined to be $100 and budget for personal expenses is an additional $1000, which implicitly means
that budget for both expenses:personal and expenses is $1100.
Transactions in expenses:personal:electronics will be counted both towards its $100 budget and $1100 of expenses:personal , and transactions in any other subaccount of expenses:personal would be
counted towards only towards the budget of expenses:personal.
For example, let’s consider these transactions:
~ monthly from 2019/01
expenses:personal $1,000.00
expenses:personal:electronics $100.00
liabilities
2019/01/01 Google home hub
expenses:personal:electronics $90.00
liabilities $-90.00
2019/01/02 Phone screen protector
expenses:personal:electronics:upgrades $10.00
liabilities
2019/01/02 Weekly train ticket
expenses:personal:train tickets $153.00
liabilities
2019/01/03 Flowers
expenses:personal $30.00
liabilities
As you can see, we have transactions in expenses:personal:electronics:upgrades and expenses:personal:train tickets, and since both of these accounts are without explicitly defined budget,
these transactions would be counted towards budgets of expenses:personal:electronics and expenses:personal accordingly:
$ hledger balance --budget -M
Budget performance in 2019/01:
|| Jan
===============================++===============================
expenses || $283.00 [ 26% of $1100.00]
expenses:personal || $283.00 [ 26% of $1100.00]
expenses:personal:electronics || $100.00 [ 100% of $100.00]
liabilities || $-283.00 [ 26% of $-1100.00]
-------------------------------++-------------------------------
|| 0 [ 0]
And with --empty, we can get a better picture of budget allocation and consumption:
$ hledger balance --budget -M --empty
Budget performance in 2019/01:
|| Jan
========================================++===============================
expenses || $283.00 [ 26% of $1100.00]
expenses:personal || $283.00 [ 26% of $1100.00]
expenses:personal:electronics || $100.00 [ 100% of $100.00]
expenses:personal:electronics:upgrades || $10.00
expenses:personal:train tickets || $153.00
liabilities || $-283.00 [ 26% of $-1100.00]
----------------------------------------++-------------------------------
|| 0 [ 0]
Envelope budgeting sets the limits on the source side, and a pile of money approaching zero is very intuitive.
Goal budgeting sets the goals (or limits) on the destination side. And we don’t usually enforce not going over.
In either system, the numbers we pick can be goals (go at least this far) or limits (don’t go beyond this).
You can also mix them: envelope/source limits for some categories, goal/destination thresholds for others. –>
Calculate return on investment
A tutorial for the roi (Return On Investment) command.
Cash-only investments
Let’s consider the easy case first, where your assets and your
investment is the same single commodity (in this case, USD), and
whenever value of your investment changes, you record the change
manually, balancing it against equity:unrealized gains.
Lets say that we found an investment in Snake Oil that is promising to give us 10% annually:
2019-01-01 Investing in Snake Oil
assets:cash -$100
investment:snake oil
2019-12-24 Recording the growth of Snake Oil
investment:snake oil = $110
equity:unrealized gains
For now, basic computation of the rate of return, as well as IRR and TWR, gives us the expected 10%:
$ hledger roi -Y --inv investment --pnl "unrealized"
+---++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=====++========++============+==========+
| 1 || 2019-01-01 | 2019-12-31 || 0 | $100 | $110 | $10 || 10.00% || 10.00% | 10.00% |
+---++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
However, lets say that shorty after investing in the Snake Oil we started to have second thoughts, so we prompty withdrew $90, leaving only $10 in. Before Christmas, though, we started to get the “fear of missing out”, so we put the $90 back in. So for most of the year, our investment was just $10 dollars, and it gave us just $1 in return:
2019-01-01 Investing in Snake Oil
assets:cash -$100
investment:snake oil
2019-01-02 Buyers remorse
assets:cash $90
investment:snake oil
2019-12-30 Fear of missing out
assets:cash -$90
investment:snake oil
2019-12-31 Recording the growth of Snake Oil
investment:snake oil = $101
equity:unrealized gains
Now IRR and TWR are drastically different:
$ hledger roi -Y --inv investment --pnl "unrealized"
+---++------------+------------++---------------+----------+-------------+-----++-------++------------+----------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=====++=======++============+==========+
| 1 || 2019-01-01 | 2019-12-31 || 0 | $100 | $101 | $1 || 9.32% || 1.00% | 1.00% |
+---++------------+------------++---------------+----------+-------------+-----++-------++------------+----------+
Here, IRR tells us that we made close to 10% on the $10 dollars that we had in the account most of the time. And TWR is … just 1%? Why?
Based on the transactions in our journal, TWR “thinks” that we are buying back $90 worth of Snake Oil at the same price that it had at the beginning of the year, and then after that our $100 investment gets $1 increase in value, or 1% of $100. Let’s take a closer look at what is happening here by asking for quarterly reports instead of annual:
$ hledger roi -Q --inv investment --pnl "unrealized"
+-------++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
+=======++============+============++===============+==========+=============+=====++========++============+==========+
| 1 || 2019-01-01 | 2019-03-31 || 0 | $10 | $10 | 0 || 0.00% || 0.00% | 0.00% |
| 2 || 2019-04-01 | 2019-06-30 || $10 | 0 | $10 | 0 || 0.00% || 0.00% | 0.00% |
| 3 || 2019-07-01 | 2019-09-30 || $10 | 0 | $10 | 0 || 0.00% || 0.00% | 0.00% |
| 4 || 2019-10-01 | 2019-12-31 || $10 | $90 | $101 | $1 || 37.80% || 1.00% | 4.03% |
+-------++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
| Total || 2019-01-01 | 2019-12-31 || 0 | $100 | $101 | $1 || 9.32% || 1.00% | 1.00% |
+-------++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
Now both IRR and TWR are thrown off by the fact that all of the growth for our investment happens in Q4 2019. Reported rates are annualized, that is IRR computation is still yielding 9.32% and TWR is still 1%, but these rates are computed over three month period instead of twelve, so in order to get an annual rate they should be multiplied by four!
Let’s try to keep a better record of how Snake Oil grew in value:
2019-01-01 Investing in Snake Oil
assets:cash -$100
investment:snake oil
2019-01-02 Buyers remorse
assets:cash $90
investment:snake oil
2019-02-28 Recording the growth of Snake Oil
investment:snake oil
equity:unrealized gains -$0.25
2019-06-30 Recording the growth of Snake Oil
investment:snake oil
equity:unrealized gains -$0.25
2019-09-30 Recording the growth of Snake Oil
investment:snake oil
equity:unrealized gains -$0.25
2019-12-30 Fear of missing out
assets:cash -$90
investment:snake oil
2019-12-31 Recording the growth of Snake Oil
investment:snake oil
equity:unrealized gains -$0.25
Would our quarterly report look better now? Almost:
$ hledger roi -Q --inv investment --pnl "unrealized"
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
+=======++============+============++===============+==========+=============+=======++========++============+==========+
| 1 || 2019-01-01 | 2019-03-31 || 0 | $10.00 | $10.25 | $0.25 || 9.53% || 2.50% | 10.53% |
| 2 || 2019-04-01 | 2019-06-30 || $10.25 | 0 | $10.50 | $0.25 || 10.15% || 2.44% | 10.15% |
| 3 || 2019-07-01 | 2019-09-30 || $10.50 | 0 | $10.75 | $0.25 || 9.79% || 2.38% | 9.78% |
| 4 || 2019-10-01 | 2019-12-31 || $10.75 | $90.00 | $101.00 | $0.25 || 8.05% || 0.25% | 1.00% |
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
| Total || 2019-01-01 | 2019-12-31 || 0 | $100.00 | $101.00 | $1.00 || 9.32% || 7.77% | 7.77% |
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
Something is still wrong with TWR computation for Q4, and if you have been paying attention you know what it is already: big $90 buy-back is recorded prior to the only transaction that captures the change of value of Snake Oil that happened in this time period. Lets combine transactions from 30th and 31st of Dec into one:
2019-12-30 Fear of missing out and growth of Snake Oil
assets:cash -$90
investment:snake oil
equity:unrealized gains -$0.25
Now growth of investment properly affects its price at the time of buy-back:
$ hledger roi -Q --inv investment --pnl "unrealized"
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
+=======++============+============++===============+==========+=============+=======++========++============+==========+
| 1 || 2019-01-01 | 2019-03-31 || 0 | $10.00 | $10.25 | $0.25 || 9.53% || 2.50% | 10.53% |
| 2 || 2019-04-01 | 2019-06-30 || $10.25 | 0 | $10.50 | $0.25 || 10.15% || 2.44% | 10.15% |
| 3 || 2019-07-01 | 2019-09-30 || $10.50 | 0 | $10.75 | $0.25 || 9.79% || 2.38% | 9.78% |
| 4 || 2019-10-01 | 2019-12-31 || $10.75 | $90.00 | $101.00 | $0.25 || 8.05% || 2.33% | 9.57% |
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
| Total || 2019-01-01 | 2019-12-31 || 0 | $100.00 | $101.00 | $1.00 || 9.32% || 10.00% | 10.00% |
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
And for annual report, TWR now reports the exact profitability of our investment:
$ hledger roi -Y --inv investment --pnl "unrealized"
+---++------------+------------++---------------+----------+-------------+-------++-------++------------+----------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=======++=======++============+==========+
| 1 || 2019-01-01 | 2019-12-31 || 0 | $100.00 | $101.00 | $1.00 || 9.32% || 10.00% | 10.00% |
+---++------------+------------++---------------+----------+-------------+-------++-------++------------+----------+
Using commodities and prices
Let’s redo the same Snake Oil example, but creating a special commodity to track amount of Snake Oil we have.
We will use SNKOIL as a commodity name, and will assume that 1 SNKOIL = $1 at the beginning of 2019.
As before, we start with a simple example where we invest in SNKOIL, and by the end of 2019 our investment growth by 10%.
2019-01-01 Investing in Snake Oil
assets:cash
investment:snake oil 100 SNKOIL @@ $100
; Recording the growth of Snake Oil
P 2019-12-24 SNKOIL $1.1
We need to tell roi that we are interested in the growth of value of
our investment with --value=then switch, which forces it to use
prices that were in effect at each moment in time that roi inspects
for its computations:
$ hledger roi -Y --inv investment --pnl "unrealized" --value=then
+---++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=======++========++============+==========+
| 1 || 2019-01-01 | 2019-12-31 || 0 | $100.0 | $110.0 | $10.0 || 10.00% || 10.00% | 10.00% |
+---++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
Following the story from the previous example, lets say that shorty after investing in the Snake Oil we started to have second thoughts, so we prompty withdrew $90, leaving only $10 in. Before Christmas, though, we started to get the “fear of missing out”, so we put the $90 back in. So for most of the year, our investment was just $10 dollars (or, rather 10 SNKOIL):
2019-01-01 Investing in Snake Oil
assets:cash
investment:snake oil 100 SNKOIL @@ $100
2019-01-02 Buyers remorse
assets:cash
investment:snake oil -90 SNKOIL @@ $90
2019-12-23 Fear of missing out
assets:cash
investment:snake oil 90 SNKOIL @@ $90
; Recording the growth of Snake Oil
P 2019-12-24 SNKOIL $1.1
These numbers do not look correct:
$ hledger roi -Y --inv investment --pnl "unrealized" --value=then
+---++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=======++========++============+==========+
| 1 || 2019-01-01 | 2019-12-31 || 0 | $100.0 | $110.0 | $10.0 || 83.66% || 10.00% | 10.00% |
+---++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
That is because the “Fear of missing out” buy-back transaction is likely incorrect: based on the price in that transaction, it look like we are buying back $90 worth of Snake Oil at the same price that it had at the beginning of the year, and then after that our investment gets sudden increase in value. This completely throws off IRR computations.
Let’s say that we kept a better record of SNKOIL prices and we can compute a more precise amount of SNKOIL we bough back at the end of the year:
2019-01-01 Investing in Snake Oil
assets:cash
investment:snake oil 100 SNKOIL @@ $100
2019-01-02 Buyers remorse
assets:cash
investment:snake oil -90 SNKOIL @@ $90
; Recording the price of Snake Oil
P 2019-02-28 SNKOIL $1.025
P 2019-06-30 SNKOIL $1.05
P 2019-09-30 SNKOIL $1.075
2019-12-23 Fear of missing out
assets:cash
investment:snake oil 83.72 SNKOIL @@ $90 ; $90/$1.075 = 83.72
; Recording the growth of Snake Oil
P 2019-12-24 SNKOIL $1.1
Now our IRR looks better:
$ hledger roi -Y --inv investment --pnl "unrealized" --value=then
+---++------------+------------++---------------+----------+-------------+--------++--------++------------+----------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+========++========++============+==========+
| 1 || 2019-01-01 | 2019-12-31 || 0 | $100.000 | $103.092 | $3.092 || 25.22% || 3.09% | 3.09% |
+---++------------+------------++---------------+----------+-------------+--------++--------++------------+----------+
Though from IRR perspective it looks like we only had about $10 worth of SNKOIL for most of the year, and still managed to get a return of $3, which looks like about a quarter of $10 invested - hence 25.22% of return from IRR standpoint. Note that TWR gives us more sensive 3.09% of return, which is closer to what you would expect, and shows that TWR is indeed (way) less sensitive to the money movements.
Investments that pay interest out (loans, bonds, dividends)
Let’s say that you have given someone a loan or put money in a savings account, or maybe bought bonds that pay out regular coupons, and now you have monthly/quarterly/annual payouts. What is the best way to record them so that we could compute ROI?
For the following example, we will assume that you put $100 into savings account that pays out $1 quarterly (so your interest is not added to your investment):
2019-01-01 Investment
assets:cash
investment:saving $100
We need to make sure that:
-
Payout transactions are included in the analysis, so they must match the query given to
--inv. This could be achieved if investment account is mentioned in our transaction. -
Payout transactions do not change the value of the investment
These two bullet points are naturally translated to this transaction:
2019-03-31 Interest
assets:cash $1
investment:saving $0
This transaction is not balanced, though. We need to balance it with the “profit and loss” account:
2019-03-31 Interest
assets:cash $1
investment:saving $0
equity:profit and loss
So, at the end of the quarter your investment grew $1 in value and that $1 was immediately paid out to you, and this transaction lines with the description pretty well.
Let’s complete the journal with one year of payouts:
2019-01-01 Investment
assets:cash
investment:saving $100
2019-03-31 Q1 Interest
assets:cash $1
investment:saving $0
equity:profit and loss
2019-06-30 Q2 Interest
assets:cash $1
investment:saving $0
equity:profit and loss
2019-09-30 Q3 Interest
assets:cash $1
investment:saving $0
equity:profit and loss
2019-12-31 Q4 Interest
assets:cash $1
investment:saving $0
equity:profit and loss
We can now compute ROI:
$ hledger roi -Y --inv investment --pnl "profit and loss"
+---++------------+------------++---------------+----------+-------------+-----++-------++------------+----------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=====++=======++============+==========+
| 1 || 2019-01-01 | 2019-12-31 || 0 | $96 | $100 | $4 || 4.06% || 4.06% | 4.06% |
+---++------------+------------++---------------+----------+-------------+-----++-------++------------+----------+
Cashflow is $100 paid in minus $4 of interest received back.
Calculate unrealized gain
This is a guide on calculating the unrealized capital gain/loss of investments,
using the balance --gain report (currently unreleased and available only in the git repo).
This guide assumes you’ve read the investments guide and that you’re using the “simple” version of recording investment transactions laid out in that document, using @ or @@. We’ll also be using a FIFO system for sales. At the end we’ll discuss how to adapt your strategy for different systems.
Buying
Let’s say you start your year with $100:
2021-01-04 opening balances
assets:cash $100.00
equity:opening/closing balances
In february you decide to start investing in a stock you’ve been monitoring:
P 2021-01-04 ABC $2
P 2021-01-11 ABC $3
P 2021-01-18 ABC $4
2021-01-19 buying stock
assets:stocks:ABC:20210119 5 ABC @ $4.40
assets:cash $-22.00
Note that the stock was not bought at the exact market price we recorded.
Looking at the gain report at this point tells us that we’ve lost $2:
$ hledger bal --gain
$-2.00 assets:stocks:ABC:20210119
--------------------
$-2.00
This is because if we sell at the last known market price, that is
what our loss will be. If we add --infer-market-price hledger will
insert a P 2021-01-19 ABC $4.40 during processing, making the gain
$0.
Price changes
Let’s say the market price grows some more in the following weeks:
P 2021-01-25 ABC $5
P 2021-02-01 ABC $6
We now see a profit of $8:
$ hledger bal --gain
$8 assets:stocks:ABC:20210119
--------------------
$8
We can also track the gain over time:
$ hledger bal --gain -W
Incremental gain in 2021-01-04..2021-02-07, valued at period ends:
|| 2021-01-04W01 2021-01-11W02 2021-01-18W03 2021-01-25W04 2021-02-01W05
============================++===========================================================================
assets:stocks:ABC:20210119 || 0 0 $-2.00 $5.00 $5.00
----------------------------++---------------------------------------------------------------------------
|| 0 0 $-2.00 $5.00 $5.00
We start out with our gain of $-2 as before. Since the value of the
stock increases by $1 every week and we bought 5 shares, we see a $5
increment in the following weeks. To see the total gain over time add
the -H option.
At this point, you decide to buy some more shares:
2021-02-02 buying more stock
assets:stocks:ABC:20210202 4 ABC @ $6.50
assets:cash $-26.00
Let’s see what this does to our gain calculation:
$ heldger bal --gain
$8 assets:stocks:ABC:20210119
$-2 assets:stocks:ABC:20210202
--------------------
$6
We still see the same gain for the first lot. The new lot has a gain of $-2, decreasing our total gain to $6.
Unfortunately, in the next week the market price of the stock decreases:
P 2021-02-08 ABC $5
Our gain report over time now looks like this:
$ hledger bal --gain -W -b 2021-01-18
Incremental gain in 2021-01-04..2021-02-14, valued at period ends:
|| 2021-01-18W03 2021-01-25W04 2021-02-01W05 2021-02-08W06
============================++============================================================
assets:stocks:ABC:20210119 || $-2.00 $5.00 $5.00 $-5.00
assets:stocks:ABC:20210202 || 0 0 $-2.00 $-4.00
----------------------------++------------------------------------------------------------
|| $-2.00 $5.00 $3.00 $-9.00
We’ll add some more prices, and then start selling our stocks.
P 2021-02-15 ABC $6
P 2021-02-22 ABC $7
Selling
We’ll start of by selling some of our first lot:
2021-02-23 sell some stock
assets:cash $12.00
assets:stocks:ABC:20210119 -2 ABC @ $4.40
income:capital gains
This leaves us with the following gain report:
$ hledger bal -W --gain -b 2021-02-07
Incremental gain in 2021-02, valued at period ends:
|| 2021-02-01W05 2021-02-08W06 2021-02-15W07 2021-02-22W08
============================++============================================================
assets:stocks:ABC:20210119 || $5.00 $-5.00 $5.00 $-0.20
assets:stocks:ABC:20210202 || $-2.00 $-4.00 $4.00 $4.00
----------------------------++------------------------------------------------------------
|| $3.00 $-9.00 $9.00 $3.80
At first glance, the negative value in the last column might seem
counterintuitive. Didn’t we just make a profit on our sale? However,
our unrealized gain decreased by the sale. The realized gain is
recorded by the income:capital gains posting. Our income statement
tells us our realized gain:
$ hledger is
Income Statement 2021-01-04..2021-02-23
|| 2021-01-04..2021-02-23
======================++========================
Revenues ||
----------------------++------------------------
income:capital gains || $3.20
----------------------++------------------------
|| $3.20
======================++========================
Expenses ||
----------------------++------------------------
----------------------++------------------------
||
======================++========================
Net: || $3.20
The total value of the remaining 3 stocks we didn’t sell increased by $3 that week, leaving us the $-0.20 figure we see in the last column of the gain report.
The next week, the price increases again, and we decide to sell all our remaining stock:
P 2021-03-01 ABC $8
2021-03-02 sell remaining stock
assets:cash $54.00
assets:stocks:ABC:20210119 -3 ABC @ $4.40
assets:stocks:ABC:20210202 -4 ABC @ $6.50
income:capital gains
This decreases our remaining unrealized gain down to 0:
$ hledger bal -W --gain -b 2021-02-07 -H
Historical gain in 2021-02-01..2021-03-07, valued at period ends:
|| 2021-02-07 2021-02-14 2021-02-21 2021-02-28 2021-03-07
============================++============================================================
assets:stocks:ABC:20210119 || $8.00 $3.00 $8.00 $7.80 0
assets:stocks:ABC:20210202 || $-2.00 $-6.00 $-2.00 $2.00 0
----------------------------++------------------------------------------------------------
|| $6.00 $-3.00 $6.00 $9.80 0
Different cost base calculations
The transactions described above could easily be adapted for LIFO: just use a different lot for the initial sale.
ACB gets a bit more difficult. If we buy more stock, we essentially have to change the cost basis of our previous lots as well. The resulting journal might look a little like this:
2021-01-04 opening balances
assets:cash $100.00
equity:opening/closing balances
P 2021-01-04 ABC $2
P 2021-01-11 ABC $3
P 2021-01-18 ABC $4
2021-01-19 buying stock
assets:stocks:ABC:20210119 5 ABC @ $4.40
assets:cash $-22.00
P 2021-01-25 ABC $5
P 2021-02-01 ABC $6
2021-02-02 buying more stock
assets:stocks:ABC:20210119 -5 ABC @ $4.40
assets:stocks:ABC:20210119 5 ABC @ $5.33
assets:stocks:ABC:20210202 4 ABC @ $5.33
assets:cash $-26.00
equity:rounding
P 2021-02-08 ABC $5
P 2021-02-15 ABC $6
P 2021-02-22 ABC $7
2021-02-23 sell some stock
assets:cash $12.00
assets:stocks:ABC:20210119 -2 ABC @ $5.33
income:capital gains
P 2021-03-01 ABC $8
2021-03-02 sell remaining stock
assets:cash $54.00
assets:stocks:ABC:20210119 -3 ABC @ $5.33
assets:stocks:ABC:20210202 -4 ABC @ $5.33
income:capital gains
If and how you deal with rounding depends on if and how you need to
report your capital gains to your tax authority. You can avoid
rounding errors by always using the @@ symbol, but this essentially
makes it impossible to sell only part of your shares (or you have to
deal with rounding at time of sale instead of time of purchase). A
journal might then look like this:
2021-01-04 opening balances
assets:cash $100.00
equity:opening/closing balances
P 2021-01-04 ABC $2
P 2021-01-11 ABC $3
P 2021-01-18 ABC $4
2021-01-19 buying stock
assets:stocks:ABC 5 ABC @@ $22.00
assets:cash $-22.00
P 2021-01-25 ABC $5
P 2021-02-01 ABC $6
2021-02-02 buying more stock
assets:stocks:ABC -5 ABC @@ $22.00
assets:stocks:ABC 9 ABC @@ $48.00
assets:cash $-26.00
P 2021-02-08 ABC $5
P 2021-02-15 ABC $6
P 2021-02-22 ABC $7
P 2021-03-01 ABC $8
2021-03-02 sell stock
assets:cash $70.00
assets:stocks:ABC:20210119 -9 ABC @@ $48.00
income:capital gains
Change account name separator
Timedot format makes me want to use dots (.) for separating account components, instead of colon (:).
For example, instead of fos:hledger:timedot I’d like to write fos.hledger.timedot.
We can use the powerful account aliases feature
to rewrite account names before hledger’s account name parser sees them.
In journal files, we can use an alias directive.
Note the backslash which tells the regular expression engine it’s a literal . not a wildcard:
# alias /REGEX/=REPLACEMENT
alias /\./=:
2008/01/01 income
assets.bank.checking $1
income.salary
Check that subaccounts are recognised:
$ hledger -f t.journal bal --no-elide
$1 assets
$1 bank
$1 checking
$-1 income
$-1 salary
--------------------
0
Alias directives aren’t supported in the timedot format,
2016/2/4
fos.hledger.timedot 2
fos.ledger 1
so we would use the --alias command line option instead.
The second backslash tells the shell that’s a literal backslash, not a shell escape sequence:
$ hledger --alias /\\./=: -f t.timedot bal --no-elide
3.00 fos
2.00 hledger
2.00 timedot
1.00 ledger
--------------------
3.00
Charts and Graphs
Tips and techniques for producing graphical charts from hledger data.
The most general, lowest-common-denominator way is to produce simple CSV output
from a report and then make charts from that using the tool of your choice.
Eg, drag the CSV file into a spreadsheet and use its charting tools.
The balance command with –layout=bare and -N/–no-total is most often used. Eg:
hledger bal --layout bare -N expenses -o expenses.csv
The quickest way to get polished, ready-to-use charts as of 2025 is probably to export to Beancount format and then use Fava, a mature web UI with ready-made charts. (If this doesn’t work, you may need to alias some account names, eg; see Beancount output):
hledger print -o export.beancount
fava export.beancount
Here are some other ways:
Charting tools built for hledger
Simplest first:
hledger-web
hledger-web has a simple balance over time chart, in the register view. (demo)
hledger web

hledger-bar
hledger-bar (2023) is a bash script for making quick bar charts in the terminal.
$ hledger bar someacct
2023-01 +++++
2023-02 ++++
2023-03 ++++
2023-04 +
$ hledger bar -v 1 -f $TIMELOG biz
2023-01 15 +++++++++++++++
2023-02 10 ++++++++++
2023-03 20 ++++++++++++++++++++
2023-04 12 ++++++++++++
$ hledger bar -v 1 -f $TIMELOG biz -p weeklyfrom3weeksago
2023-03-27W13 8 ++++++++
2023-04-03W14 2 ++
2023-04-10W15 4 ++++
2023-04-17W16 5 +++++
hledger-plot
hledger-plot (2023) is a powerful chart-making tool written in python.

hledger-lit
hledger-lit (2025) is a nice python web UI that makes several kinds of chart.
hledger-sankeymatic
At https://sankeymatic.com you can make flow diagrams in your browser,
from data records like Source [Amount] Target.
Here’s a rough script that exports the outflows from several asset accounts:
LEDGER_FILE=examples/sample.journal
for f in checking saving cash; do
hledger areg $f -O tsv | tail +2 | sed -e "s/^/$f\t/"
done | cut -f1,6,7 | gsed -E -e 's/\$//' -e 's/([^\t]*)\t([^\t]*)\t([^\t]*)/\1 [\3] \2/' | grep '\[-' | gsed 's/\[-/[/'
hledger-sankeymatic is a better script to help you export hledger data to SankeyMATIC.
hledger-sankey
- https://github.com/adept/hledger-sankey is a python script using pandas and plotly to plot three sankey graphs of hledger data
- https://github.com/JustSaX/hledger-sankey is hledger-sankey made interactive with Streamlit

hledger-sankey-visualization
https://github.com/AlessandroW/hledger-sankey-visualization is a javascript that reads hledger’s JSON output, and uses amCharts.

hledger-vega
hledger-vega (2022) is a set of scripts for producing custom charts from your hledger reports, using the powerful vega-lite.

r-ledger
r-ledger is an R package for making reports and charts from hledger, Ledger or Beancount.

Grafana export script
Visualise your finances with hledger, InfluxDB, and Grafana (2017)

docker-finance metadata visualising
docker-finance, a hledger-based system, can visualise large quantities of metadata using ROOT.

Other charting tools
Ledger chart tools
Tools built for Ledger can sometimes be adapted to work with hledger also; or, hledger data can be exported to Ledger-readable files. Eg:
- Report Scripts for Ledger CLI with Gnuplot (2016)
- ledger-plots (2018), an R package that makes PDF charts from Ledger
- More can be found at the PTA site: GUI, web, mobile, other
ploterific
ploterific (stack install hvega-theme ploterific) produces simple charts,
in a HTML file that uses the Vega-Lite javascript library.
Charts can also be saved as SVG or PNG. Here’s a detailed example:
hledger -f examples/bcexample.hledger bal -O csv -N expenses -3 cur:USD \
| sed 's/ USD//' \
| ploterific -m Bar -f account:N -f balance:Q -c account -o a.html \
&& open a.html
Let’s break down that command line:
-f examples/bcexample.hledger- use this example file in the hledger repo. Omit this to use your default journal.bal- run a balance report-O csv- show it as CSV on stdout-N- disable the final Total rowexpenses- limit to accounts whose name containsexpenses-3- summarise accounts to depth 3 and abovecur:USD- limit to balances inUSDcurrency. If you use the$symbol, it would becur:\\$.sed 's/ USD//g'- process the output with sed, stripping theUSDsymbols to leave bare numbers for ploterific. With$it would besed 's/\$//g'.-m Bar- useBaras the Vega-Lite mark type-f account:N- use theaccountcolumn as the first feature (X axis), treating values as names-f balance:Q- use thebalancecolumn as a second feature (Y axis), treating values as quantities-c account- useaccountvalues to select colours-o a.html- save into a temporary HTML file&& open a.html- and view it in your web browser, on Mac; on other systems it might bexdg-openorstart
Here is the same chart but with the colour set by the balance:
hledger -f examples/bcexample.hledger bal -O csv -N expenses -3 cur:USD \
| sed 's/ USD//' \
| ploterific -m Bar -f account:N -f balance:Q -c balance:Q -o a.html
Checking for errors
hledger can check your data in various ways.
Built in checks
In hledger 1.21+, see strict mode and the check command.
Old way to check accounts
Here’s another way to check for undeclared accounts, that works with older hledger versions, showing some diff tricks:
$ diff -U0 --label "Unused Accounts" --label "Undeclared Accounts" <(hledger accounts --declared) <(hledger accounts --used)
Compare report output
Save the output of a report, and later use diff to compare the
output of the same report, revealing any changes.
$ hledger COMMAND > report.txt
$ hledger COMMAND > report2.txt
$ diff report.txt report2.txt
Or, periodically commit a report’s output into your version control system, then use the VCS to detect any changes since the last commit.
$ hledger COMMAND > report.txt; git add report.txt; git commit -m 'report' report.txt
$ hledger COMMAND > report.txt; git diff -- report.txt
Sometimes new data can cause noisy diffs, especially in tabular reports. To minimise diffs, here are some things to try:
When comparing [balancesheetequity] diffs, do any of these help:
-
–layout wide,MAXWIDTH, –layout bare, –layout tidy
-
not using tree mode if you’re using that
-
not showing multiple periods if you’re using that
-
overriding commodity styles with -c, eg limiting precision
-
limiting –depth
-
-MA –summary
-
excluding particular problem accounts/commodities
-
generating one report per commodity with cur: ?
-
generating csv/tsv/json output ?
-
using the balance command instead, and eg –transpose, or a single –period and –format
-
using the [a]register command
A pre-commit hook
Version control systems often support a “pre-commit hook”, a script which is required to succeed before each commit. Eg:
#!/bin/bash
set -e
hledger check -s
Flycheck mode
If you use Emacs, you can configure flycheck to run your preferred checks when you edit a journal file. This integration is currently quite basic, but it still gives very useful real-time feedback. Setup tips:
- in Emacs, install the
flycheck-hledgerpackage and customise theflycheck-checkersvariable, addinghledgerto the list - customise the
flycheck-hledger-strictandflycheck-hledger-checksvariables
Todo / maybe
Here are some checks we don’t support, but could:
- accountsactive - for each account used, if there is posting with an
open:tag, it must have a corresponding posting with aclose:tag, and all other postings must be chronologically between (and if on the same date, textually between) open and close postings. (“Accounts are posted to only within their declared active period.”) - explicitamounts - all transaction amounts have been recorded explicitly
Preventing postings to closed accounts
Some options (from https://www.reddit.com/r/plaintextaccounting/comments/180a11b/how_to_represent_closure_of_an_account_using):
-
Add a balance assertion when the account is closed, and then use
hledger check recentassertions(https://hledger.org/1.31/hledger.html#other-checks). After a week has passed, new postings to it (without a new, correct, balance assertion) will raise an error. -
Add a zero balance assertion in the far future. Eg:
12000-01-01 * This balance shall remain zero for ten thousand years assets:foo2022 0 ==* 0
-
(If yearly granularity is ok:) Use a new journal file and new account declarations, and drop the obsolete accounts, each year. Strict mode or
hledger check accountswill warn of any new references to the old account. -
(If you are comfortable with unix tools:) Make a custom check script, eg grep the output of
hledger reg OLDACCTfor inappropriate dates. -
https://hledger.org/scripts.html#hledger-check-fancyassertions might help.
-
support account lifetimes ? #1389 is the place to work on better support for this.
Common journal entries
Here are entries for some common transactions. Check other pages, or https://wiki.plaintextaccounting.org for more detailed examples.
Shopping
2017/1/26 market
expenses:food $10
assets:cash
Invoicing, accrual basis
2018-04-16 * (2018-001) SuperCompany invoice
Revenue:Software Development $ -2420.00
Assets:Receivable:SuperCompany $ 2420.00
2018-04-26 * (2018-001) SuperCompany payment
Assets:Receivable:SuperCompany $ -2420.00 = $0
Assets:Checking $ 2420.00
Invoicing, cash basis
; Invoices aren't tracked in cash basis, use unbalanced postings to track them anyway.
2018-04-16 * (2018-001) SuperCompany invoice
(Assets:Receivable:SuperCompany) $2420
2018-04-26 * (2018-001) SuperCompany payment
(Assets:Receivable:SuperCompany) $-2420 = $0
Revenue:Software Development $-2420
(Liabilities:Tax:2018) $-420
Assets:Checking:Estimated Tax Savings:2018 $420
Assets:Checking $2000
The above plus postings to track and save estimated income tax:
2018-04-26 * (2018-001) SuperCompany payment
(Assets:Receivable:SuperCompany) $-2420 = $0
Revenue:Software Development $-2420
(Liabilities:Tax:US:2018) $-420
Assets:Checking:Tax:US:2018 $420
Assets:Checking $2000
Tracking a mortgage
2019/01/01 Buy House
Assets:House 500,000.00
Liabilities:Mortgage
2019/02/01 Mortgage Payment
Liabilities:Mortgage 1,000.00
Expenses:Interest:Real Estate 833.33
Assets:Cash -1833.33
2019/03/01 Mortgage Payment
Liabilities:Mortgage 1,002.00
Expenses:Interest:Real Estate 831.33
Assets:Cash -1833.33
2019/03/01 Zillow Price Estimate
Assets:House = 505,000.00 ; assign new balance, generating a transaction
Equity:Unrealized Gains
Converting time to money
Perhaps you track billable hours with hledger, and you’d like to see those hours as money. The method depends on where you store time records:
Journal files
If you might have a different rate for each job,
and there aren’t too many transactions per job:
you could record the rate as a cost (2h @ $100),
and run a cost report with -B/--cost.
When the rate doesn’t change much and there are many transactions,
it’s more convenient to declare it with a P directive,
and run a value report with -V/-X.
Timeclock files
You can’t write P directives in timeclock format, but you can include the timeclock file(s) from a journal file, and write the directives there. (See below.)
Timedot files
Timedot files can’t hold P directives, so include
them from a journal file, which can.
Also timedot amounts normally have no commodity symbol, making them hard to price;
but you can give them one with the D directive:
# 2022-time.journal
D 1.0 h
P 2022-01-01 h $100
include 2022.timedot
# 2022.timedot
2022-01-01
a ....
$ hledger -f 2022-time.journal bal
1.0 h a
--------------------
1.0 h
$ hledger -f 2022-time.journal bal -V
$100 a
--------------------
$100
If your rate changes, start a new timedot file:
# 2022-time.journal
D 1.0 h
P 2022-01-01 h $100
include 2022.timedot
P 2022-03-01 h $120
include 2022b.timedot
$ hledger -f 2022-time.journal bal -MVTA
Balance changes in 2022Q1, valued at period ends:
|| Jan Feb Mar Total Average
===++===================================
a || $100 0 $120 $220 $73
---++-----------------------------------
|| $100 0 $120 $220 $73
Cost notation
Here is a little more background on cost notations, moved here from hledger manual > Cost reporting.
Essentially there are four ways to record a commodity conversion in hledger (as of 2023):
Conversion with implicit cost
Let’s assume 100 EUR is converted to 120 USD. You can just record the outflow (100 EUR) and inflow (120 USD) in the appropriate asset account:
2021-01-01
assets:cash -100 EUR
assets:cash 120 USD
hledger will assume this transaction is balanced, inferring that the
conversion rate must be 1 EUR = 1.20 USD. You can see the inferred
rate by using hledger print -x.
Pro:
- Concise, easy
Con:
- Less error checking - typos in amounts or commodity symbols may not be detected
- Conversion rate is not clear
- Disturbs the accounting equation, unless you add the –infer-equity flag
You can prevent accidental implicit conversions due to a mistyped
commodity symbol, by using hledger check commodities.
You can prevent implicit conversions entirely, by using hledger check balancednoautoconversion, or -s/--strict.
Conversion with explicit cost
You can add the conversion rate using @ notation:
2021-01-01
assets:cash -100 EUR @ 1.20 USD
assets:cash 120 USD
Now hledger will check that 100 * 1.20 = 120, and would report an error otherwise.
Pro:
- Still concise
- Makes the conversion rate clear
- Provides more error checking
Con:
- Disturbs the accounting equation, unless you add the –infer-equity flag
Conversion with equity postings
In strict double entry bookkeeping, the above transaction is not
balanced in EUR or in USD, since some EUR disappears, and some USD
appears. This violates the accounting equation (A+L+E=0), and prevents
reports like balancesheetequity from showing a zero total.
The proper way to make it balance is to add a balancing posting for each commodity, using an equity account:
2021-01-01
assets:cash -100 EUR
equity:conversion 100 EUR
equity:conversion -120 USD
assets:cash 120 USD
Pro:
- Preserves the accounting equation
- Keeps track of conversions and related gains/losses in one place
- Standard, works in any double entry accounting system
Con:
- More verbose
- Conversion rate is not obvious
- Cost reporting requires adding the –infer-costs flag
Conversion with equity postings and explicit cost
Here both equity postings and @ notation are used together.
2021-01-01
assets:cash -100 EUR @ 1.20 USD
equity:conversion 100 EUR
equity:conversion -120 USD
assets:cash 120 USD
Pro:
- Preserves the accounting equation
- Keeps track of conversions and related gains/losses in one place
- Makes the conversion rate clear
- Provides more error checking
Con:
- Most verbose
- Not compatible with ledger
Create a journal
See also: Input and Setting LEDGER_FILE and Starting a journal file in the hledger manual.
There are lots of ways to start and update a journal file:
with touch
The simplest possible journal is just an empty file:
$ touch 2023.journal
The name doesn’t matter much and can be changed later.
One file per year is common,
and so is a .journal or .hledger extension.
with cat
$ cat >>2023.journal
2023-01-26
expenses:food $10
assets:cash
<CTRL-D>
Account names can be anything
and you can change them later by search and replace.
If you don’t know what to choose,
start with these five:expenses, income, assets, liabilities, and equity,
perhaps with one extra subcategory as above.
with a text editor
Write transactions in a text editor, optionally using an editor mode, and save the file.
with hledger add
Use the interactive add command and follow the prompts:
$ hledger add
with hledger-iadd
- ensure $LEDGER_FILE exists
hledger iadd- enter one or more transactions
with hledger-web
- ensure $LEDGER_FILE exists
hledger web- wait for web browser to open
- click “add transaction” or press “a”
- enter a transaction, click ok or press enter
Cryptocurrency tax reporting
(This is the README in the hledger repo’s examples/csv/cctax/ directory, also published as the Cryptocurrency tax reporting page on hledger.org. The information here was last updated in 2025-12. )
In this directory we are not concerned with importing to hledger, but with exporting from hledger to cryptocurrency tax calculators.
Why do this ? In most countries, the revenue and capital gain from receiving/selling/trading cryptocurrencies (cc) is taxable and must be reported. There are several aspects to this:
- Calculating income. This is easy - at least, once you have recorded all transactions in hledger.
- Calculating capital gains. This can be hard, and depends on your country’s rules.
- Calculating taxes owed. This also depends on your country’s rules, and is often handled by specialised tax preparation software.
Centralised exchanges will calculate gains for you, but self-custodied wallets, decentralised exchanges, or other defi apps won’t; with those, you are responsible for calculating gains.
In some countries, such as the USA, this requires:
- tracking the acquisition date, cost, and wallet of every purchase/acquisition over your lifetime
- tracking these across movements/splits/merges
- disposing (selling/spending) them in a required order, such as:
- FIFO - first in first out
- LIFO - last in first out
- HIFO - highest cost first out
- LOFO - lowest cost first out
- SpecId - specific identification of lots
- and thereby calculating the capital gains or losses.
In the US, before 2025 universal cost tracking was used to determine the disposal order; this means you consider the acquisition costs/dates of all your holdings of an asset (cryptocurrency), across all wallets. From tax year 2025, per-wallet cost tracking is required, which means you apply the disposal order separately within each wallet. The “Safe Harbor” rule allows you to allocate pre-2025 lots to specific wallets (once), which could help optimise taxes in some cases.
In the UK, it’s easier: you use each asset’s average cost across all wallets.
There are several ways you could calculate gains. Eg, assuming the worst case (US taxes), you could:
-
Calculate gains using only hledger, by keeping track of each lot and lot movement with subaccounts, and selecting the appropriate lots/costs in disposal transactions. This is intuitive and robust. But also tedious, and above a certain level of activity it becomes unmanageable.
-
Calculate gains using the built-in lot tracking syntax of Ledger or Beancount. (hledger doesn’t have this feature.) Beancount’s is more robust. These may be too limited to calculate gains accurately, eg when there are inter-wallet transfers. There may be additional plugins which help.
-
Online cryptocurrency tax calculators (Bitcoin.Tax, Cointracker, Coinledger, Koinly, Summ, TokenTax..) These let you upload transaction history from all of your cryptocurrency activities. Then they analyse this data, identify what happened (you may need to add configuration here), and provide gains reports. These apps are convenient and featureful, eg they may understand your country’s tax rules; but they also add rather serious privacy risk. When they are hacked or infiltrated, potentially your entire past, present and future cryptocurrency activities can be analysed.
-
Offline cryptocurrency tax calculators. These tend to be more private.
- BittyTax - UK and US variants (can do wallet-based cost tracking by using multiple configs)
- rotki - freemium, featureful, does not support US yet (wallet-based cost tracking, https://github.com/rotki/rotki/issues/2438)
- RP2 - does not support US yet (https://github.com/eprbell/rp2/issues/135)
The main focus in this directory is exporting to offline calculators. Examples of their CSV import format are collected here. Each record represents an event in a particular cryptocurrency “wallet” (on an exchange or on a blockchain). There are basic events like deposit, withdrawal, buy, sell, expense, income; and some more specialised event types.
Currency conversion
I wrote two currency conversion explainers in 2021, now both combined on this page, showing different ways of recording conversions. See Cookbook > Multiple currencies for more.
Here are various ways of recording a conversion from one currency or commodity to another.
Implicit conversion rate
Let’s say we are exchanging cash with a friend:
2021-07-27 give dollars, get euros
assets:cash USD -10.00
assets:cash EUR 8.50
hledger understands that 10 dollars were converted to 8.50 euros.
A conversion rate is inferred automatically so as to make the transaction balance.
(This can be seen with hledger print -x.)
This is easy to write and to understand.
However it is not a fully correct double-entry-bookkeeping journal entry,
since USD has magically transformed into EUR “in flight”.
It is also error prone, since a typo in either amount may not be detected.
For example, we might forget the decimal point and write USD -1000.
Also, it is easy to create such entries accidentally.
For example, in one posting within a transaction we might mistype or omit the currency symbol.
hledger accepts these implicit conversions by default, for convenience and compatibility.
But you can disallow them by using
strict mode
or by running the check command
(eg: hledger check balancednoautoconversion).
Explicit conversion rate
We can record the conversion rate (cost of one currency in the other) explicitly.
This adds redundancy, allowing hledger to catch more errors,
and also makes the rate more explicit to human readers.
We can write the total amount with @@
(convenient when entries are complex):
2021-07-27 give dollars, get euros
assets:cash USD -10.00 @@ EUR 8.50
assets:cash EUR 8.50
or the per-unit amount with @
(makes the exchange rate, or an investment’s cost basis, clearer):
2021-07-27 give dollars, get euros
assets:cash USD -10.00 @ EUR 0.85
assets:cash EUR 8.50
hledger calls these “costs”. This is the most common way to record such transactions.
Conversion using equity
A fully correct double-entry-bookkeeping journal entry
avoids the PTA-specific @/@@ notation,
and is balanced in each commodity, using equity. Eg:
2021-07-27 give dollars, get euros
assets:cash USD -10.00
equity:conversion USD 10.00
assets:cash EUR 8.50
equity:conversion EUR -8.50
or, letting hledger infer the above:
2021-07-27 give dollars, get euros
assets:cash USD -10.00
assets:cash EUR 8.50
equity:conversion
This is discussed more here.
Two entries for one conversion
This comes up eg when converting Paypal CSV, which provides two records for a currency conversion, one for each side. For example, given some paypal CSV rules like:
account1 assets:paypal
...
if %type (T0200|General Currency Conversion)
description %type for %referencetxnid %itemtitle
account2 equity:conversion
we might see journal entries like:
2020-05-16 * Liberapay donation to simonmichael (team darcs-hub)
assets:online:paypal C$24.56 =* C$24.56
revenues:foss donations:darcshub C$-26.00
expenses:banking:paypal C$1.44
2020-05-16 * General Currency Conversion for 8Y4N723T948333034 Liberapay donation to simonmichael (team darcs-hub)
assets:online:paypal C$-24.56 =* C$0.00
equity:conversion C$24.56
2020-05-16 * General Currency Conversion for 8Y4N723T948333034 Liberapay donation to simonmichael (team darcs-hub)
assets:online:paypal $16.88 =* $17.55
equity:conversion $-16.88
Ie: some canadian dollars received, followed by two transactions converting that balance to US dollars. This is equivalent to the “Fully balanced conversion” above, just with the conversion entry split into two.
Balancing the accounting equation
If you are a fan of the accounting equation and like to check it by seeing
a zero total in the balancesheetequity report,
you will need to do something with these equity:conversion balances.
Such as, converting them to retained revenues/expenses in your local currency.
This can perhaps be done at transaction time,
or at the end of each accounting period,
or with report options.
Best practice is not yet clear, suggestions welcome.
Converting with equity or with costs
Here’s another look at the equity vs costs style of conversion entries, somewhat repeating the content above.
In a currency conversion or a stock purchase/sale, one commodity is exchanged for another. In plain text accounting, there are two ways to record such conversions:
Equity method
Balance both commodities against an Equity account. Eg:
2021-01-01
assets:usd -1.20 USD
equity:conversion 1.20 USD
equity:conversion -1.00 EUR
assets:eur 1.00 EUR
or, equivalently:
2021-01-01
assets:usd -1.20 USD
assets:eur 1.00 EUR
equity:conversion
Cost method
PTA tools provide the @ (or @@) notation for specifying a conversion cost (essentially; Ledger/Beancount also provide an alternate {} notation):
2021-01-01
assets:usd -1.20 USD
assets:eur 1.00 EUR @ 1.20 USD
@-priced amounts (the 1.00 EUR above) will be converted to their price’s commodity (USD)
- internally for checking transaction balancedness, always
- and visibly in reports, when the
-B/--costflag is used.
Note the redundancy in this entry; the two amounts and the @ price must agree. This provides some extra error checking, but you can also write it non-redundantly, by omitting an amount:
2021-01-01
assets:usd ; the -1.20 USD amount is inferred
assets:eur 1.00 EUR @ 1.20 USD
or the conversion price:
2021-01-01
assets:usd -1.20 USD
assets:eur 1.00 EUR ; the @ 1.20 USD price is inferred
Which is better ?
It depends…
Cost reporting
The @ (conversion price) method allows “cost” reporting. By adding the
-B/--cost flag you can easily see what things cost (or were sold
for) in the other commodity. Eg:
$ hledger -f 2a.j bal --cost assets:eur
1.20 USD assets:eur
--------------------
1.20 USD
Capital gain/loss reporting
The equity method keeps a trace of all commodity exchanges in the equity account, in effect properly recording the accumulated gain/loss from all commodity exchanges (it can be seen by valuing the accumulated total of those equity balances in some commodity).
The @ method does not record the gain/loss from commodity exchanges (at least, not so explicitly and not grouped by commodity pair. We can still calculate it using hledger valuation features like -V, –valuechange, –gain.)
The accounting equation
The equity method keeps accounts and the accounting equation (A+L+E=0) balanced. See how it keeps the balance report’s total as zero:
$ hledger -f 1a.j bal
1.00 EUR assets:eur
-1.20 USD assets:usd
-1.00 EUR
1.20 USD equity:conversion
--------------------
0
The @ method causes unbalanced accounts and a non-zero total (because of the “magical” transformation from one commodity to the other):
$ hledger -f 2a.j bal
1.00 EUR assets:eur
-1.20 USD assets:usd
--------------------
1.00 EUR
-1.20 USD
The zero total can be seen only if all amounts are converted to cost:
$ hledger -f 2a.j bal --cost
1.20 USD assets:eur
-1.20 USD assets:usd
--------------------
0
Summary
The equity method:
- doesn’t support cost reporting.
The @ method:
- doesn’t support easy gain/loss reporting by commodity pair.
- doesn’t maintain balanced accounts
However, hledger nowadays can convert between these styles on the fly, using --infer-equity or (with some restrictions) --infer-costs.
See the hledger manual > Cost reporting for the latest on this.
Editors
If you edit your journals (and other hledger data files) with a text editor, you want that frequent task to be as pleasant and non-tedious as possible. So it’s worth using a powerful text editor - one with comfortable copy/paste, search & replace, and perhaps more advanced features like macros.
For the popular text editors there are helper modes/extensions which can make editing
hledger journal files much more convenient. These provide things like
syntactic highlighting, auto indentation, and tab completion of
account names. You can find a list of these extensions at
https://plaintextaccounting.org/#editor-plugins.
The ones with “hledger” in their name are designed specifically for working with hledger journals, while the ones with “ledger” in their name are not, but can often work well with hledger as well (eg: ledger-mode).
Here are more details and tips.
Emacs
Ledger-mode
https://github.com/ledger/ledger-mode (manual), for Emacs, is the most used and maintained helper mode for hledger and Ledger files.
It has some hard-coded dependence on Ledger’s command-line interface, so does not work perfectly with hledger, whose CLI is similar but not identical.
1. Basic configuration:
(setq ledger-binary-path "hledger"
ledger-mode-should-check-version nil
ledger-report-auto-width nil
ledger-report-links-in-register nil
ledger-report-native-highlighting-arguments '("--color=always"))
(add-to-list 'auto-mode-alist '("\\.hledger\\'" . ledger-mode))
Or, using use-package:
(use-package ledger-mode
:custom
((ledger-binary-path "hledger")
(ledger-mode-should-check-version nil)
(ledger-report-auto-width nil)
(ledger-report-links-in-register nil)
(ledger-report-native-highlighting-arguments '("--color=always")))
:mode ("\\.hledger\\'" "\\.ledger\\'"))
This configuration makes ledger-mode commands run hledger, configures colored output correctly and turns off some incompatible functionality. Most editing functions in the journal buffer work correctly and so do most of the reports. Annoyingly, reconcilliation, quick balance display and the add-transaction prompt fail due to hardcoded ledger-specific options that hledger doesn’t have. Hence the following:
2. Compatibility script
Further functionality can be gained by putting the following script in your PATH and pointing `ledger-binary-path` to it.
(setq ledger-binary-path "hledger.sh")
(setq ledger-default-date-string "%Y-%m-%d")
The script works by converting command line options and arguments to hledger’s equivalent incantations, or filtering them out. By using it all the commands in the journal buffer become functional. It enables the Quick Balance Display (<C-c> <C-p>), display-ledger-stats (<C-c> <C-l>) and Add Transaction (<C-c> <C-p>) and gets us a step closer to reconcilliation (still not there tho).
#!/bin/sh
# hledger.sh
# Kudos to acarrico for the original script.
# The script is contingent on ledger-default-date-string
# being set to "%Y-%m-%d", aka ISO date format.
iargs=("$@")
oargs=()
j=0;
date=;
for((i=0; i<${#iargs[@]}; ++i)); do
case ${iargs[i]} in
--date-format)
# drop --date-format and the next arg
i=$((i+1));
;;
cleared) # for ledger-di
splay-balance-at-point
# convert "cleared" to "balance -N -C"
oargs[j]=balance; oargs[j+1]=-N; oargs[j+2]=-C; j=$((j+3));
;;
xact)
# convert "xact" to "print --match"
oargs[j]=print; oargs[j+1]=--match; j=$((j+2));
# drop xact argument and stash the date argument
i=$((i+1));
date=${iargs[i]};
;;
# NOTE: Reconciliation still doesn't work for other reasons
# so the following filters/conversions are unnecessary for now.
# --sort) # for reconcilliation
# # drop --sort and the next arg
# i=$((i+1));
# ;;
# --uncleared) # for reconcilliation
# # convert "--uncleared" to "--unmarked --pending"
# oargs[j]=--unmarked; oargs[j+1]=--pending; j=$((j+2))
# ;;
*)
# keep any other args:
oargs[j]=${iargs[i]};
j=$((j+1));
;;
esac
done
if test "$date"
then
# substitute the given date for the old date:
hledger "${oargs[@]}" | sed "1s/....-..-../$date/"
else
# echo "${oargs[@]}"
hledger "${oargs[@]}"
fi
TODO – Generalize script to work with different date formats
Feature Compatibility Checklist:
- Ledger Buffer
- Navigation
- Completion
- Set effective date (<C-c> <C-t>)
- Quick balance display (<C-c> <C-p>) – FAILS due to -–date-format, fixed by script
- Copy Transaction (<C-c> <C-k>)
- Clear Posting/Transaction
- Delete transaction
- Sorting transactions
- Narrowing (i.e. regex filtering)
- Transaction completion by payee (<C-c> <TAB>)
- Add transaction (<C-c> <C-a>) – FAILS due to -–date-format, fixed by script
- display-ledger-stats (<C-c> <C-l>) – FAILS due to -–date-format, fixed by script
- Report Buffer (<C-c> <C-o> <C-r>)
- balance
- register
- payee – FAILS, hledger uses @item syntax for argument files, ledger uses @payee to filter transactions
- account
- custom
NOTE: custom report simply opens the minibuffer with the word ledger prefilled. It works if you input a valid hledger command, but it doesn’t use the current buffer filename or anything. It does update on buffer changes however. Naming and saving custom reports works as well.- Expansion formats
- %(ledger-file)
- %(payee)
- %(account)
- %(tag-name) – TODO: Figure out what/why.
- %(tag-value) – TODO: Figure out what/why.
- %(month)
- Jump to transaction from register report – FAILS, line data is passed to emacs using ledger’s -–prepend-format
- Reversing report order
- Expansion formats
- Reconcile Buffer – FAILS due to an assortment of errors
NOTE: Opening the reconcile buffer uses Narrowing (<C-c> <C-f>) to filter transactions to only ones relevant to the selected account. After the reconcile buffer errors out filtering needs to be turned off with <C-c> <C-f>. - Scheduling Transactions – FAILS – ledger/hledger UI
incompatibility
NOTE: ledger has a scheduler for periodic transactions which are written in a separate ledger file. Ledger-mode provides a way to view and copy those transactions into the main journal, making the process of updating the journal with periodic transactions somewhat easier. The closest corresponding functionality are hledger’s periodic transactions and the –forecast option (how easy it would be to adapt ledger-mode to it remains to be seen).
TODO – Add common hledger reports as custom reports
#367 ledger-mode setup for hledger needs documenting has
more tips to be collected here.
hledger-related issues
Errors:
- The –date-format error:
ledger-mode hardcodes this option into it’s ledger incantations. It can be avoided in some, but not all cases. Generally the first and most common error you bump into. - –columns: see #279
This error is why (setq ledger-report-auto-width nil) is necessary. - –prepend-format:
This is why (setq ledger-report-links-in-register nil) is necessary.
ledger-mode links register report entries to transactions by prepending line data to ledger’s output, then stripping that data from the buffer and turning the output to links. Hledger doesn’t have a –prepend-format CLI option. - command emacs is not recognised.
This is the next hurdle to reconcilliation. ledger-cli has an option to output data, formatted for elisp consumption, which the reconcile buffer uses.
hledger-mode
https://github.com/narendraj9/hledger-mode
An alternative to ledger-mode, written specifically for hledger. Has some different features. Less actively maintained.
flycheck-hledger
https://github.com/DamienCassou/flycheck-hledger (2021) provides realtime indication of problems in your journal. It can be used with ledger-mode or hledger-mode.
C-x ` steps to the next problem in the current file.C-u C-x ` restarts the scan from the top.
A description should appear in the message area,
but Emacs may hide it behind “…locus…” messages;
you can fix that by customising the next-error-verbose variable to off.
Sample config:
(use-package flycheck-hledger
:after (flycheck ledger-mode) ; or hledger-mode
:ensure t
:demand t
:custom
(flycheck-hledger-strict t)
(flycheck-hledger-checks '("ordereddates" "recentassertions")) ; extra checks from https://hledger.org/hledger.html#check: ordereddates, uniqueleafnames, payees, recentassertions, tags..
;(flycheck-hledger-executable "hledger")
)
Currently flycheck-hledger always runs hledger with the --auto flag,
so be aware that any auto posting rules will be active.
flymake-hledger
https://github.com/DamienCassou/flymake-hledger/ (2023) is a more recent alternative to flycheck-hledger, using the built-in Flymake rather than the third-party Flycheck feature.
Here are some early configuration notes, using use-package:
;; Enable verbose use-package debug info when starting with --debug-init
;; (when init-file-debug
;; (setq use-package-verbose t
;; use-package-expand-minimally nil
;; use-package-compute-statistics t
;; debug-on-error t))
;; Configure flycheck-like keybindings for flymake:
(use-package flymake
:bind (
:map flymake-mode-map
("C-c ! n" . flymake-goto-next-error)
("C-c ! p" . flymake-goto-prev-error)
("C-c ! l" . flymake-show-buffer-diagnostics) ; a new list for each buffer, unlike flycheck
("C-c ! v" . flymake-switch-to-log-buffer)))
(use-package flymake-hledger
:load-path "~/src/flymake-hledger"
:after (ledger-mode flymake)
:hook (
(ledger-mode . flymake-hledger-enable)
;; Make C-x ` work ?
;; XXX Both of these work only in the first file opened; debugging needed.
;; (ledger-mode . (lambda () (setq next-error-function 'flymake-goto-next-error)))
;; (ledger-mode . (lambda () (setq next-error-function (lambda (num reset) (when reset (goto-char (point-min))) (flymake-goto-next-error num)))))
)
:custom
(flymake-show-diagnostics-at-end-of-line t) ; might require Emacs 30
(flymake-suppress-zero-counters t)
(flymake-hledger-checks '("accounts" "commodities" "balancednoautoconversion" "ordereddates")) ; "recentassertions" "payees" "tags" "uniqueleafnames" https://hledger.org/hledger.html#check
)
To extra checks for a specific journal file, add a line like this near the top:
; -*- eval:(add-to-list 'flymake-hledger-checks "recentassertions" t); -*-
org babel
org babel (ob) is the system for evaluating code blocks embedded in org outlines. ob-hledger.el adds hledger support, so that hledger reports can be embedded within an org outline, and easily evaluated inline or exported in various formats. If you have many reports, this can be a nice way to save and organise and interactively update them.
In 2021 a number of org addons including ob-hledger.el were moved from org to the separate org-contrib package. As of 2025 the primary hosting for this seems to be https://git.sr.ht/~bzg/org-contrib. Here are the ob-hledger.el changes.
To enable org-contrib in emacs:
-
M-x list-packages, install org-contrib -
In your emacs config, add:
(require 'ob-hledger)
(and evaluate it withC-M-x, or a restart) -
In any org file, add hledger commands like this:
#+begin_src hledger :cmdline -f ~/finance/2022.journal balance #+end_src -
To evaluate the command inline, press
C-c C-cwith point (cursor) inside the above line -
To update all such reports in the file, press
C-c C-v b -
To export all reports:
- as html, and open in browser:
C-c C-e h o - as html:
C-c C-e h h - as UTF-8 text:
C-c C-e t U - as markdown (if configured):
C-c C-e m o - etc.
- as html, and open in browser:
-
To export only the reports in the current subtree:
- configure it at top of org file:
# -*- org-export-initial-scope:subtree; -*-) - put point in the desired subtree before exporting as above
- configure it at top of org file:
See also Using Ledger for Accounting in Org-mode with Babel
calc
Calc can help perform arithmetic on amounts in the buffer during data entry.
Position the cursor anywhere in the number, then use C-x * w to enter embedded Calc mode, and q to exit.
Eg to halve an amount: put cursor at the number, then C-x w * 2 / q
Note Calc rewrites decimal numbers to a standard format, hiding trailing zeros by default, which you may not want. To preserve trailing zeros, you must force a fixed number of decimal digits. In calc mode (eg before pressing q above) do:
d f 2 returnto force two digits (eg shows .00)d f 3 returnto force three digits (eg shows .005 when there’s a half cent)d nto choose normal mode (shows all digits except trailing zeros)
And to ensure that such config changes are saved in (and reloaded from) ~/.emacs.d/calc.el rather than the current buffer, add this to your emacs config:
(setq calc-mode-save-mode 'save)
Misc
A helper to browse TODO tags in the journal:
(defun journal-todos nil (interactive) (lgrep "TODO:" "current.journal" "~/finance" nil))
Vim
vim-ledger
https://github.com/ledger/vim-ledger
hledger-vim
https://github.com/anekos/hledger-vim
timedot-vim
https://github.com/linuxcaffe/timedot-vim
Neovim
Run :TSInstall ledger to enable nvim-treesitter for hledger.
VS Code
Extensions for working with hledger journal files - as of 2025Q3 I think they rank in features and activity roughly like so:
- hledger Language Support by evsyukov (source) 2025
- HLedger Formatter by iiatlas (source) 2025
- hledger (Tools) by patrickt (source) 2024
- hledger-vscode by mark-hansen (source) 2017
Extensions for working with hledger CSV rules files:
- hledger rules (source) by EthanOlpin 2025
All VS Code extensions mentioning “hledger”:
Miscellaneous
- https://github.com/mondeja/hledger-fmt a hledger journal formatter; can integrate with VS Code or other editors
From the mail list, a trick for aligning transaction amounts: “Space-indent the account, tab-indent the amount, set a large tab stop.”
Exporting from hledger
A number of hledger reports support multiple output formats, such as plain text, HTML, JSON, beancount, or SQL. There are also tabular formats such as CSV, TSV, and FODS. See hledger: Output format for full details.
Here are some common ways to export hledger data.
Export journal entries with print
You can print whole journal entries. This usually preserves the most information. txt (AKA hledger journal), beancount, HTML or JSON output formats are good choices here. CSV/TSV/FODS are also supported but there will be multiple records per transaction, which you may need to recombine later.
Examples:
$ hledger print date:2025 -o 2025.json
$ hledger print -o all.beancount
Check the output format docs for additional tips, eg Beancount output may need account aliases.
Export account transactions with aregister
You can export a register (list of changes, plus running balance) with hledger’s register command. You should probably export the activity of just one account and one currency at a time. Eg:
$ hledger -f examples/sample.journal reg -O csv checking cur:'\$'
"txnidx","date","code","description","account","amount","total"
"1","2008-01-01","","income","assets:bank:checking","$1","$1"
"2","2008-06-01","","gift","assets:bank:checking","$1","$2"
"3","2008-06-02","","save","assets:bank:checking","$-1","$1"
"5","2008-12-31","","pay off","assets:bank:checking","$-1","0"
But the aregister command may be better for this, because it guarantees one record per transaction even with complex multi-posting transactions, and it shows the other account names (abbreviated), making categorisation easier later:
$ hledger -f examples/sample.journal areg checking -O csv cur:'\$'
"txnidx","date","code","description","otheraccounts","change","balance"
"1","2008-01-01","","income","in:salary","$1","$1"
"2","2008-06-01","","gift","in:gifts","$1","$2"
"3","2008-06-02","","save","as:ba:saving","$-1","$1"
"5","2008-12-31","","pay off","li:debts","$-1","0"
Export report data
You can export the output of other reports, like a balance sheet or income statement. Eg:
$ hledger bs -o bs.html
$ open bs.html
$ hledger is -o is.tsv
If you export as CSV or TSV, you can drag those files into a spreadsheet.
If your spreadsheet can read FODS format, use that instead, to preserve more formatting.
Other
- hledger2psql is a tool that exports a hledger journal to a postgres database.
- Interfacing with real-life human accountants (hledger list, 2023)
Frequently Asked Questions
If you have additions or suggestions, just click the “edit this page” link at the bottom, or let us know in the chat.
Here are some common questions:
- How does hledger relate to Ledger ?
- Isn’t plain text hard to work with ?
- What helpers are available for my text editor or IDE ?
- In Emacs, do I have to use hledger-mode ? Is ledger-mode only for Ledger ?
- Isn’t importing from banks a pain ?
- Where can I find hledger CSV rules for my financial institutions ?
- There are so many docs, where do I even..
All questions:
ACCOUNTING
What’s accounting ?
Accounting means keeping track of the flow and whereabouts of things you value, such as money or time, and using this information for insight, planning and decision-making. Here’s our Accounting basics and Accounting: further study pages.
Why might I want to do accounting ?
For clarity, control, planning, accountability, compliance, tax reporting, tax audits. It clarifies activity, priorities, obligations, opportunities.
What’s double-entry accounting ?
Double-entry bookkeeping is the traditional method for keeping accounting records reliably. For every movement of value (a transaction), both the source and destination are recorded. These are labelled “Credit” and “Debit”, to minimise working with negative numbers. Simple arithmetic invariants help prevent errors.
What’s plain text accounting ?
You can read more about Plain Text Accounting (PTA) at https://plaintextaccounting.org. In short, it is a way of doing Double Entry Bookkeeping (DEB) and accounting on a computer, using simple text files and small flexible tools, rather than databases and big applications. Minus and plus signs are usually used instead of Credit and Debit notation, making it easier to learn than traditional DEB. The text files are human-readable and easy to convert or to manage with version-control tools.
Is there another plain text accounting FAQ ?
Why yes there is: you may want to also check the general https://plaintextaccounting.org/FAQ.
THE HLEDGER PROJECT
What’s hledger ?
One of the best tools for doing Plain Text Accounting. It’s free and you can read all about it at the https://hledger.org home page.
Why was hledger created ?
I needed to make working on finances (and financial tools) more fun for myself. Ledger was incomplete and stalled, using/supporting/community-building around it was frustrating, and I wanted to work with Haskell not C++. My goals included:
- Provide a more usable, robust, documented, cross-platform-installable version of Ledger.
- Provide a more maintainable and hackable version of Ledger for developers.
- Provide a useful library and toolbox for finance-minded Haskell programmers.
- Explore the suitability of Haskell for such applications.
- Attempt to build a successful, solvent, self-supporting project in a thriving ecosystem of financial software projects.
A 2025 comment:
HN> When Gnucash did something, I was never sure what actually happened. And sometimes it would crash. Was all my data safe? Hard to tell.
You express it exactly, I felt the same way with Gnucash, Quicken, and all the other non-plain-text accounting apps I tried. Finance was stressful enough without also worrying about messed-up data. That was perhaps the biggest motivation to switch to Ledger, when I found it.
Later, I often could not figure out how to make Ledger do something I knew it could do, and I often was surprised by a crash or wrong behaviour when it saw some new combination of features and data that hadn’t been tested or implemented yet. This was a big motivation to write hledger.
See also: hledger and Ledger: History.
What is the hledger project’s current mission and plans ?
- Help make plain text accounting more usable and useful for all.
- Bring relief to people experiencing financial and financial technology stress, by providing dependable, empowering accounting tools, learning materials, and community.
- Help people and communities in all countries increase their financial mastery and freedom.
- Help grow a shared global culture of accountability and sustainability.
- Starting with this project and ourselves.
Here is the ROADMAP.
HLEDGER AND PLAIN TEXT ACCOUNTING
We use another system, we don’t need this ?
Every tool has strengths and weaknesses. hledger is lightweight, flexible and relatively easy to glue into other systems; it might be worth exploring as a complementary tool.
How do you collaborate with accountants and the non-PTA world ?
Depending on their needs, you send them a few standard reports (balance sheet, income statement, itemized account registers or a full transaction journal)
- as plain text (optionally spruced up with your own templates)
- or as HTML
- or as PDF
- or as CSV they can import into Excel and elsewhere
Must I enter data in a text editor ?
No. A good text editor can be a very efficient way to work on your data, but there are other ways:
- use a terminal-based data entry tool like hledger add or hledger-iadd
- use a web-based data entry tool like hledger-web
- use a phone-based data entry app like MoLe
- import CSV data, avoiding manual data entry.
What is a good set of account names to start with ?
The chart of accounts (list or tree of account names) will vary according to your language and your accounting situation, and will usually evolve as your needs change. Currently hledger does not have any built-in default chart of accounts. You can start small: name just the accounts you need, as you need them. Or, copy a chart of accounts that seems suitable, from other accounting software or from the web. Some examples can be found at https://plaintextaccounting.org/Choosing-accounts.
What can hledger do for me ?
See Why hledger ? for capabilities and use cases.
How could that help me ?
- More clarity, transparency and accountability, for yourself or others
- Know what you owe, or who owes you
- Know where the money went; steer your spending
- Know how you spent your time; easy client invoicing
- More foresight and ability to plan; avoid overdrafts, late fees, cashflow crunches
- Know all the numbers you need for tax reporting; know how much to save for estimated taxes
- Less stress, fear or overwhelm
- More satisfaction, empowerment, and prosperity!
Isn’t manual data entry a pain ?
- Not if you spend a few minutes every day.
- Not if the benefits are worth it to you.
- Not if you use a comfortable editor and copy/paste a lot.
- Not if you use tools to help (editor modes, hledger add, hledger-iadd, hledger-web..)
- Not if you use rules to generate your recurring transactions.
Isn’t importing from banks a pain ?
Not once you have set up a manual or automated routine for it. The possibilities for automation vary by bank and country, but the following semi-manual workflow is almost always possible and quick:
- Manually download recent CSVs from your bank’s website
hledger import ACCT1.csv ACCT2.csv ...- review/clean up the new entries in your journal.
Isn’t plain text hard to work with ?
No way, it’s great, honest. We love it. You’ll love it. It’s fast. It’s cheap. It’s non-distracting. It keeps you focussed on the content. It’s copy-pasteable. It’s accessible to screen readers. It’s resizable. You can pick the font and colours. You do not need “Plaintext Reader, Trial Version” to read it. you do not need “Plaintext Studio Pro” to write it. You can use your favorite editor and skills you already have. You can search in it! You can version control it. It works well over remote/slow connections. It’s future-proof. It will be just as usable in 15 or 50 years. You can still read it even without the right software or (if you print it) a working computer. “Accounting data is valuable; we want to know that it will be accessible for ever - even without software. We want to know when it changes, and revision-control it. We want to search and manipulate it efficiently. So, we store it as human-readable plain text.”
Isn’t this too weird for my family, business partners, tax accountant to use ?
Maybe. You can ask them to enter data via hledger-web, or import from their mobile expenses app or a shared spreadsheet. You can show them the hledger-web UI, or HTML reports, or give them CSV to open in a spreadsheet.
HLEDGER AND OTHER SOFTWARE
How does hledger relate to Ledger ?
hledger (begun in 2006) is inspired by, and a friendly coopetitor of, John Wiegley’s Ledger (begun in 2003). Ledger is the pioneer whose brilliant design and data formats defined this new software genre.
hledger is an attempt to recreate Ledger in a more expressive programming language and take it to a higher level of usability and practicality. hledger provides high Ledger compatibility and it’s possible to keep your data files readable by both. See hledger and Ledger.
How does hledger relate to Beancount ?
Beancount (begun in 2008) is the other “big 3” PTA app. Written in the popular Python programming language, it’s notable for best-in-class capital gains tracking (as of 2025) and an excellent web app (Fava).
Beancount’s file format is different from h/Ledger’s. Beancount can export to hledger format, with some manual fixups required. hledger can export to Beancount format, allowing hledger data to be viewed in Fava. See hledger and Beancount.
How is hledger different from / interoperable with… ?
See Cookbook > Other software for notes on Ledger, Beancount, GnuCash, Quickbooks, etc. Also:
- PTA apps, showing project stats
- Syntax Quick Reference for the Ledger-Likes, showing several file formats
- PTA app comparisons, though mostly very out of date
How could I import/migrate from…
- Look for a relevant page at Cookbook > Other software
- Otherwise, try to export CSV representing transactions - one record per transaction - and then use hledger’s CSV reader. Also look for pre-existing CSV rules you can use.
- Or, make a custom script or workflow to convert the old data to hledger’s simple journal format.
How could I export/migrate to…
- Look for a relevant page at Cookbook > Other software
- See also Exporting from hledger.
GETTING STARTED
There are so many docs, where do I even..
You’re right. You can skip them: just run hledger for help.
When you’d like a bit more guidance, use the Docs page, and/or the chat room.
If you want the most complete reference, use the hledger manual. If you read this and nothing else, you’ll be a hledger expert. But it is rather large; so maybe read the table of contents, the first sections, and just skim the rest so you know where to find out more.
You can jump to topics in the manual with a url like hledger.org/hledger#time-periods.
(This makes a good web browser keyword;
eg in your browser, define hm TOPIC as a shortcut for https://hledger.org/hledger#TOPIC.)
The website also has a search feature.
To read a local copy of the manual, see hledger help -h.
Where can I find non-english docs ?
Unfortunately, I know of very few non-english docs for hledger. There must be some I don’t know about, please contribute any good links.
Could you help translate some docs ? Please get in touch.
Otherwise, you could try Google Translate or similar.
There are couple of non-english videos at PTA: Videos and Docs: Videos.
How do I set the default file to something other than ~/.hledger.journal ?
See hledger manual: Setting LEDGER_FILE.
I’m on Windows, how do I keep my files in AppData\Roaming ?
You could set the LEDGER_FILE environment variable (see the link in the answer above).
Or, you can create a file at the default location, that includes the rest.
You can use relative include paths, like this
(the first file must be named .hledger.journal, the other file names can be anything):
# \Users\USER\.hledger.journal
include AppData\Roaming\hledger.journal
# \Users\USER\AppData\Roaming\main.journal
include other.journal
# ...
# \Users\USER\AppData\Roaming\other.journal
# ...
Why do I get an error with non-ascii text ?
hledger uses the system’s text encoding when reading non-ascii text.
If no system encoding is configured, or if the data’s encoding is different, hledger will give an error.
Run hledger setup to check this, and use the text encoding it recommends.
Or see Install: Text encoding for more detail.
How should I organise files ?
See PTA FAQ: How should I organise files ?.
What account names should I use ?
See PTA FAQ: How should I choose account names ?.
Why are my revenue (income), liability, and equity balances negative ?
It’s normal; it’s because hledger and most other plain text accounting tools
use negative and positive numbers instead of credit and debit terminology.
Certain hledger reports (balancesheet, incomestatement, cashflow)
and flags (--invert) can show them as positive when needed.
See:
- Accounting basics for PTA users
- PTA FAQ: Where are debits and credits ?
- PTA FAQ: Why do some balances look negative ?
JOURNALS
What helpers are available for my text editor or IDE ?
See Editors.
In Emacs, do I have to use hledger-mode ? Is ledger-mode only for Ledger ?
No and no. hledger-mode is not very actively maintained. ledger-mode is more popular, more mature, and works very well with hledger and hledger journals, except for a few non-essential features, and is what hledger’s maintainer uses. See Editors for configuration tips.
How can I tidy a journal file ?
Method 1: use Emacs’s ledger-mode.
This is the least destructive way, but it requires Emacs and ledger-mode.
Select the whole journal, or a region, or move the cursor to a specific transaction, and press M-q.
Method 2: use hledger’s print command.
You might need to work around some current limitations:
-
Only transactions are printed, not directives or inter-transaction comments. Workaround: group directives at the start of the file, and copy them to the new output, and don’t use inter-transaction comments.
-
Transactions will be printed in date order. Workaround if you don’t want to sort them all by date: tidy just a subset of transactions at a time (using
-Iif needed). Eg in Emacs: select region,C-u M-S-| hledger -If- print. -
If you have
includedirectives, the included files’ transactions will also be printed. Workaround: tidy one file at a time, temporarily commenting out include directives. -
Amounts will be right-aligned, within each transaction but not across all transactions. Workaround if you want whole-file alignment and more alignment options, like aligning on decimal mark: use ledger-mode.
The print method has some inconveniences, but also extra power,
eg to select a subset of transactions with a query,
or to transform the data with --explicit, --infer-costs, --alias, --commodity-style, --pivot, etc.
Note, any time you do mass edits to valuable data, it’s wise to keep a backup and check your changes.
Eg by comparing reports before and after,
by running your usual hledger checks,
or by using a version control system.
Here are some examples using print:
cp $LEDGER_FILE $LEDGER_FILE.bak
hledger print > $LEDGER_FILE.new && mv $LEDGER_FILE.new $LEDGER_FILE
Or using the sponge helper for updating a file in place:
hledger print | sponge $LEDGER_FILE
A one-liner that preserves directives and inter-transaction comments (though not their positions):
(grep -E '^([#;]|[[:alpha:]])' $LEDGER_FILE; hledger print) > $LEDGER_FILE.new
Why did hledger rename Ledger’s “effective dates” to “secondary dates” ?
To avoid confusions like: Which is the effective date ? Which should be the later date ? What does effective mean ? If there are more than two dates of interest, what to call the others ?
Are secondary dates and posting dates the same ? Which should I use ?
These are independent features, which can be used separately or together. Both can be applied to a similar use case (transactions which have more than one date of interest). Secondary dates are best avoided IMHO, and they are deprecated in hledger.
More detail:
Here’s a secondary transaction date (https://hledger.org/hledger.html#secondary-dates):
2025-08-30=2025-08-27 * pay credit card
liabilities:credit-card $10
assets:checking
or:
2025-08-30 * pay credit card ; date2:2025-08-27
liabilities:credit-card $10
assets:checking
Here’s a posting date (https://hledger.org/hledger.html#posting-dates):
2025-08-30 * pay credit card
liabilities:credit-card $10 ; date:2025-08-27
assets:checking
or:
2025-08-30 * pay credit card
liabilities:credit-card $10 ; [2025-08-27]
assets:checking
Here’s a secondary posting date:
2025-08-30 * pay credit card
liabilities:credit-card $10 ; date2:2025-08-27
assets:checking
or:
2025-08-30 * pay credit card
liabilities:credit-card $10 ; [=2025-08-27]
assets:checking
Here’s primary and secondary dates for the transaction and each posting, all used at once:
2025-09-01 * pay credit card ; date2:2025-09-02
liabilities:credit-card $10 ; date:2025-09-03, date2:2025-09-04
assets:checking ; date:2025-09-05, date2:2025-09-06
or:
2025-09-01=2025-09-02 * pay credit card
liabilities:credit-card $10 ; [2025-09-03=2025-09-04]
assets:checking ; [2025-09-05=2025-09-06]
and their effect:
$ hledger print date:2025/9/1
2025-09-01=2025-09-02 * pay credit card
liabilities:credit-card $10 ; [2025-09-03=2025-09-04]
assets:checking ; [2025-09-05=2025-09-06]
$ hledger print date2:2025/9/2
2025-09-01=2025-09-02 * pay credit card
liabilities:credit-card $10 ; [2025-09-03=2025-09-04]
assets:checking ; [2025-09-05=2025-09-06]
$ hledger reg
2025-09-03 pay credit c.. li:credit-card $10 $10
2025-09-05 assets:checking $-10 0
$ hledger reg --date2
2025-09-04 pay credit c.. li:credit-card $10 $10
2025-09-06 assets:checking $-10 0
Both features are non-standard tool-specific notations added to standard Double Entry Bookkeeping. They make money “disappear” temporarily, unbalancing the accounting equation during the period between the two dates. But for most people this doesn’t matter, and being able to represent multiple dates precisely with one journal entry (the alternative is multiple entries updating a settlement account) is convenient.
But the docs linked above explain why I think you should avoid secondary dates. In particular, they require you to choose between reporting modes, and they tend to break balance assertions (because balances depend on the dates of postings). Posting dates don’t have this problem.
Why does this entry give a “no amount” error even though I wrote an amount ?
2019-01-01
a $1
b
Because there’s only a single space between a and 1,
so this is parsed as an account named “a 1”, with no amount.
There must be at least two spaces between account name and amount.
Why does this journal fail strict account checking even though I declared all accounts ?
account assets:bank:checking ; my bank account
account equity ; equity
2023-01-01
equity
assets:bank:checking $1000
Because there’s only a single space between assets:bank:checking and the ; comment,
so the comment is parsed as part of the account name. (hledger accounts shows this.)
There must be at least two spaces between an account name and anything that follows it.
Why do some directives not affect other files ? Why can’t I include account aliases from a sub file ?
Directives vary in their scope, ie which journal entries and which input files they affect. The differences are partly due to historical accident, and partly by design, so that reordering files, or adding another file, does not change their meaning. See journal format > Directives and multiple files. Related discussion: #217, #510, #1007.
Can I use arithmetic expressions in journal entries ?
Eg, Ledger-style amount expressions ? Generally no, hledger doesn’t allow this. (If you want it, you could revive PR #934.)
In some cases, you can emulate it with auto postings,
which can multiple amounts by a constant. Eg to (effectively) divide all food expenses by 2,
you could add this rule, and enable it with --auto:
## Add a posting subtracting half from every food expense (good enough for expense reports)
= revenues
(expenses:food) *-.5
Related: 50% of the balance?
How could I record call/put stock options ?
Here’s a suggestion from the beancount mail list (reddit).
CSV
Where can I find hledger CSV rules for my financial institutions ?
Look for a suitable rules file in the examples/csv/ directory on github. Or you can get the hledger source and look in examples/csv/.
If your financial institution is not there yet, please use these for inspiration, ask in the chat for help, and perhaps send a pull request contributing your new rules.
How do I make new hledger CSV rules ?
See the Importing CSV tutorial and the hledger manual > CSV format. (After checking for a pre-existing rules file in examples/csv/ in the hledger repo.) If possible, add your new rules file to that directory and send a pull request.
When importing CSV from two bank accounts, and there’s a transfer between them, how do I avoid duplicate entries ?
You can have one of the rules files skip that transaction. Eg if you have checking and savings CSVs, in savings.csv.rules you might have a rule like
## already imported from checking
if TRANSFER FROM .*CHECKING
skip
Other options: you could remove one of the entries manually. Or you could import both entries, but have them transfer to and from an imaginary third account (but this creates extra noise in your journal and reports).
Can I do arithmetic calculations in CSV rules ?
Eg, dividing the amount by 2 ? No; currently the best thing is to preprocess the CSV, adding the calculated values as a new field, before converting it with hledger.
Where can I find more help with CSV conversion ?
Study the whole CSV section in the manual carefully. Working with CSV has useful tips, eg for setting amounts. And of course, make use of the chat.
REPORTING
Why am I seeing some amounts without an account name in reports ?
When an account has a multi-commodity balance, hledger’s default balance, print, and register reports, like Ledger’s, will show the balance on multiple lines, with each commodity on its own line, but with the account name appearing only once (either top- or bottom-aligned, depending on report). For a clearer report, try balancesheet, incomestatement or cashflow, and/or --layout=bare, or restrict the report to a single currency with cur:SYMBOL.
Another reason you might see amounts without an account name: dropping too many account name parts with --drop.
How do I control the number of decimal places displayed ?
To set that temporarily, use the -c/--commodity-style option (one for each commodity, as needed).
Eg, this shows dollars with two decimal places, ADA with six, and EUR with none:
hledger -c '$1000.00' -c '1000.000000 ADA' -c 'EUR 1000.' bal
To make it permanent, use commodity directives.
How do I display a decimal mark different from the one in the input file ?
Use -c/--commodity-style options (one for each commodity) to override the display style(s).
Eg hledger bal -c '$1,00' displays dollar amounts with comma decimal marks,
even if they use period decimal marks in the journal.
How do I report by financial year, not calendar year ?
Just specify the desired start date, eg hledger is -Y -b 2020/4/15 or hledger is -p 'yearly from 2020/4/15'.
(With hledger <1.29, you can approximate this with -p 'every 12 months from 2020/4.
or -p 'every 365 days from 2020/4/15'.)
How can I report (or generate transactions) on every Nth … of …, etc. ?
Use a period expression with one of the more complex report intervals.
How can I report (or generate transactions) on the last day of each month ?
Use “every 31st day”. Eg:
hledger balancesheet -p 'every 31st day'
How do I show just deposits, or just withdrawals, in an account ?
Use amt: to match on amount sign. Usually you’ll want to add an account query as well:
hledger register cash 'amt:>0' # cash inflows
hledger register cash 'amt:<0' # cash outflows
If you use aregister, it’s like this (because of aregister’s special first argument):
hledger aregister cash cash 'amt:<0'
How do I show the full entries for an account’s deposits/withdrawals ?
Expanding the above reports to show the full transactions is difficult, because print is a
transaction-based report, whose query matches if any postings are matched.
For now the best approach is to run hledger print -x ACCT and filter the output yourself,
keeping only transactions where ACCT and a negative (or positive) amount appear on the same line.
This requires a tool such as awk, and a regular expression that’s good enough for your data.
It’s difficult to do this 100% reliably, avoiding false matches in descriptions, comments, subaccount names, balance assertions, commodity symbols.
But here’s an example that will work for most of us:
Show transactions where a cash account was increased:
hledger print -x cash | awk '/:cash.*? [^-=]*[0-9]/' RS="\n\n" ORS="\n\n"
or decreased:
hledger print -x cash | awk '/:cash.*? [^=]*-/' RS="\n\n" ORS="\n\n"
You can also do this kind of filtering with C-c C-f in Emacs ledger-mode, which is easier.
New: with a hledger dev build, you can now do
hledger print any:'cash amt:>0'
How do I show transactions between one account and another account ?
To show one transaction per line:
Use aregister ACCT1, with the other account as the query:
hledger aregister checking expenses:tax
To filter by direction, add 'amt:>0' or 'amt:<0':
hledger aregister checking expenses:tax 'amt:>0'
To see transactions in full:
Use print with an expr: query (requires hledger >=1.30):
hledger print expr:'checking AND expenses:tax'
Or with hledger <1.30, you can emulate that with not:not::
hledger print checking not:not:expenses:tax
How do I show a register or balance report between one account and another ?
The above won’t work with the register or balance commands because
these are posting-based reports - their query selects postings, not whole transactions.
Instead, run a print report to select transactions to use in a second report, like so:
hledger print checking | hledger -f- -I register expenses:tax
hledger print checking | hledger -f- -I balance expenses:tax
hledger print liabilities:mastercard | hledger -f- -I incomestatement
Complex multi-account transactions could muddle these reports a little; if that’s a problem you might need to exclude those transactions or split them up.
What are some gotchas with piping hledger print into another hledger command ?
hledger print reproduces transactions, but not directives.
The output will normally be a valid journal, but it can have a different meaning or even be unparseable due to:
- Loss of
decimal-markdirectives, which could disrupt number parsing. - Loss of
accountdirectives declaring accounts’ types, which could alter reports. - Balance assertions which break because you have excluded transactions they depend on.
- (In hledger <1.50:) Loss of
commoditydirectives declaring display precisions, which could disrupt transaction balancing.
Workarounds:
-
Whenever you use
-f -to readhledger printoutput, also add-Ito ignore balance assertions. And write these flags separately (-If-does not work). So:$ hledger print ... | hledger -f- -I ... -
Also recreate any required directives in the input stream. This is often not needed, but it depends on your data. If needed:
-
Keep those directives in their own file if possible (if directive scope rules allow it), which you can use as a second input file:
$ hledger print ... | hledger -I -f - -f 2023-declarations.journal ... -
Or find another way to pass the required directives along. Eg:
$ { hledger print ...; grep '^[acd]' $LEDGER_FILE; } | hledger -I f- ... $ { hledger print ...; grep '^[acd]' `hledger files` --no-filename; } | hledger -f- -I ...
-
CUSTOMISING
How can I save and reuse common options/arguments ?
Save them in a text file, with one option or argument per line,
then include @FILE in your hledger command line.
See argument files.
This is superseded by…
Is there a config file ?
Yes, since hledger-1.40. See Config files.
How can I automatically use a journal file in the current directory ?
Perhaps you have journals in multiple project directories.
If you use a standard filename, such as default.journal,
you can run hledger with the -f default.journal option or set LEDGER_FILE to default.journal, perhaps in a script.
You can also use file glob patterns, like *.journal, in -f or LEDGER_FILE.
For more flexibility, you could use direnv to customise LEDGER_FILE per directory.
hledger will not search parent directories for a journal file, though there’s probably a tool that will.
Related: #2194
With the new config file support, you could add -f FILE to a hledger.conf file in or above this directory.
SCRIPTING
How do I install more hledger scripts and add-on commands ?
git clone the hledger repo, and add the bin/ directory to your shell’s PATH. See Scripts and add-ons.
How do I make new hledger scripts ?
Install the example Scripts and add-ons and find a suitable one to copy and modify. Also see Scripting. If your new script can be useful to others, consider contributing it with a pull request.
What’s a good way to manage hledger scripts ?
Once you start defining handy scripts, they tend to proliferate and become hard to remember.
One solution is to gather scripts into a Makefile,
and run them with make SCRIPT.
This is very traditional and very common, but it has downsides.
Listing scripts, handling arguments, portability, and dealing with make’s special syntax are all a hassle.
As someone who has used make for a few decades, I urge, nay beg, you to install and use just instead. It is much better for this job and complements hledger and PTA very well. See the example Justfile and hledger and just.
HLEDGER-UI
With hledger-ui in iTerm2 on mac, why does Shift-Up/Shift-Down move the selection instead of adjusting the report period ?
iTerm2 by default doesn’t recognise SHIFT-UP/SHIFT-DOWN keys correctly. (If this has changed in recent releases, please let us know.) Here’s one way to fix it: iTerm2 > CMD-i > Keys > Key Mappings > Presets -> select “xterm Defaults” (not “Terminal.app Compatibility”).
Why hledger ?
Why should you consider using hledger ? See below, and also the FAQ.
TLDR
hledger is free, user-friendly accounting software designed to be:
Dependable - Your data stays local, private, version-controlled, and future-proof. Plain text files work with your favorite text tools and never lock you in. Features work as you’d expect. Built with a high-assurance programming language.
Fast and lightweight - Starts instantly, processes thousands of transactions per second. Single executable, no database, no configuration required.
Versatile - Track money, investments, cryptocurrencies, time, or any commodity using battle-tested double-entry bookkeeping. Scales from personal use to business accounting.
Actively maintained - New releases quarterly since 2008, extensive documentation, active community. We’ll pay you $50 if you find or fix a regression bug.
How is it different from traditional accounting software ?
hledger is a plain text accounting system, which means:
-
Data is stored in simple plain text files, which can be easily read by humans, tracked with version control software such as Git, and maintained with text processing tools. This facilitates auditing, portability, and longevity of your valuable accounting data.
-
The data format is flexible and easy to write or generate, but hledger can check it and prevent many kinds of error. This, plus the transparency and version control, provides confidence in your data and reports.
-
Data and software is kept on your local computer, keeping your financial data private and under your control. But if you should want to collaborate, version control makes that easy.
-
You can edit data with your favourite text editor or IDE, or a data entry UI, or convert and import data from other formats (eg CSV from your banks).
-
There is a fast command-line interface, which makes the tool easy to script, automate, and integrate into custom workflows.
-
The model of operation is simple: put a log of transactions in, get reports out.
-
It is fast, lightweight, non-distracting, and great for learning more of double-entry bookkeeping and accounting.
How is it different from other plain text accounting apps ?
hledger has for many years been the most actively developed and supported PTA app.
It strives to also be the easiest to use, most robust, and best documented.
See also:
What can hledger do ?
hledger can provide clarity and insight into your personal or business finances, time logs, or other dated quantitative data. You provide a list of transactions (plain text or CSV), and hledger generates reports and interactive views.
For your goals
It can help you to:
- understand bookkeeping
- keep your financial records safe, correct, and private
- untangle accounting messes
- know your financial status
- keep track of loans, due dates, invoices
- forecast future activity and balances
- search your financial history
- categorise your spending
- understand your patterns
- make impactful changes and plans
- track spending and saving goals
- answer financial questions
- explore what-if scenarios
- produce detailed time reports
- get data needed for tax preparation
- convert/transform financial data
- check the correctness of other systems
- automate accounting tasks
- create more clarity, balance, and peace of mind
Core capabilities
hledger can:
- list transactions, payees, currencies/commodities, accounts, statistics
- show account hierarchies and subaccounts
- show transactions affecting any account with running balances
- make balance sheets, cashflow reports, income statements
- show simple bar charts by period
- show purchase costs/selling prices and market values in any currency
- calculate rates of return for investments
- make reports from timeclock, timedot, or CSV/SSV/TSV files
- filter by time period, account, or any criteria
- show multiple periods side by side
- summarise accounts to any depth
- rewrite or pivot account names to transform reports
- output as plain text, HTML, CSV, TSV, JSON, SQL
- run as a live-updating terminal UI for fast interactive exploration
- run as a web app for remote/multi-user browsing and data entry
- run as a JSON web API for custom integrations
- combine multiple files and data sets
- generate recurring transactions and forecasts by rule
- generate extra postings (splits) on transactions by rule
- make budget reports showing goals vs actual performance
- generate interest transactions by rule
- help you enter new transactions with prompts or a terminal UI
- import and convert transactions from banks and other sources
- be used as a library in Haskell scripts or programs
More examples: Charts and Graphs • HTML reports • Working with other software • Investing and trading • Scripts and addons
What problems does hledger solve ?
Here are some problems a plain text accounting app, and hledger specifically, can solve. Do any of these apply to you ?
I am a technical person wanting to track finances with familiar tools and a minimum of fuss
You can learn hledger’s syntax quickly, manage data with your favourite text processing tools, and produce useful financial reports right away with a single executable requiring no configuration.
I don’t have an effective accounting system, and am experiencing financial chaos, loss, or worry
(This was my situation when I started writing hledger.) I can tell you that having some kind of system to keep a record of financial activity and give clarity and insight is a huge help; it can be life-changing. hledger is a free, highly dependable accounting tool, that scales smoothly from basic to advanced needs. It provides structure, extensive documentation, and a supportive community. It is lightweight and minimal, so you can start simply without feeling overwhelmed by complex software.
I’m worried I may accidentally lose my financial records (paper or digital).
hledger data takes up no physical space. Because the files are plain text and relatively small, it’s easy to copy them and to make redundant backups, and to use a version control system providing a clear history of changes. Multiple independent software apps can read them, and you could relatively easily build a new one. They are also human-readable and printable, useful even without software or computers.
I do single entry accounting; sometimes reports are wrong, I can’t rely on them
You can do single entry with hledger too, if you like. But for centuries, the standard for serious accounting has been double entry, because it is so good at preventing errors. hledger, like other plaintext accounting apps, is built around double entry accounting. This provides great reassurance that your reports are rock solid, so you can confidently make decisions and take actions based on them.
I am confused by accounting and bookkeeping, and I want to get better at it
Using a plain text accounting app like hledger, you’ll be working hands-on with double entry bookkeeping and general journal entries, with a minimum of distracting software and jargon. Also you’ll have a supportive community of fellow learners and experts to answer questions.
I want to try Plain Text Accounting, but I am finding it hard to get started
Perhaps you’ve struggled to get a productive Beancount setup.
Or you weren’t able to figure out how to do some things with Ledger.
You are finding it too much work to produce basic financial reports at the command line.
Maybe you want a GUI or TUI, that’s simple to install and use.
You want more up to date and complete documentation, or active chat rooms where you can get quick support.
hledger aims to serve both techies and non-techies and to provide a smoother getting started experience,
with standard financial reports and simple UIs that work out of the box.
My paper-based system is too much work and too hard to get insights from
hledger can optionally import bank data, reducing the work of manual data entry. With a little programming you can automate many accounting tasks. And hledger provides many useful and flexible reports.
My accounting spreadsheet has grown too large, too error prone, or too limiting
hledger provides a simple, expressive, widely used syntax for describing any kind of financial transaction, using standard double entry bookkeeping. It scales up to large files, provides extensive error checking, and provides very flexible reports.
My accounting software is expensive, invasive, and hard to leave
As GPLv3-licensed Free Software, hledger both is free in price, and also works to preserve your personal freedoms. It runs entirely under your control, on your private machine. You can see all of its code, and you can improve it or fix it yourself, or you can hire someone to do that. There’s no lock-in, and there are multiple ways to export data, including to other plain text accounting apps like Ledger, Beancount, Fava, Paisa etc.
My accounting software works only with an online connection
hledger is local software that works offline. The user manuals are also built in and viewable on any machine. The only time you might need the internet is to download bank data or to run your own hledger-web server, and these are optional.
My accounting software is opaque or unreliable, and I worry about messing up my data
Most hledger operations are read only and can’t affect your data. Commands that change your files are clearly identified, and often are append-only. hledger provides extensive error checking to help you avoid data errors, and the app itself is heavily tested and written in a high assurance programming language. Best of all, the plain text data files are ideal for version control. This means you can review and audit every change to your data, and undo or roll back history at any time.
My accounting software is inefficient
Is it slow to start up ? Are certain operations tedious and time-consuming ?
hledger is lightweight and fast, starting instantly and usually producing results in a fraction of a second even on old machines.
It stores data as human-readable plain text files, which means you can do even large-scale bulk edits quickly and easily.
The command line interface means you can turn complex operations into easy commands, or integrate it in automated scripts.
My accounting software is not improving, and I would prefer a tool that’s actively maintained and supported
hledger has delivered a new major release every quarter since 2017. It’s the only PTA app with active chat rooms (matrix and IRC). It’s the most-used PTA app in some circles.
I need to track time and produce accurate time reports
hledger has built-in support for two time logging formats, so you can produce detailed time reports just as easily as financial reports.
Key characteristics
Easy to get started
Plain Text Accounting apps are usually slightly technical, but hledger aims to be intuitive, learnable and highly usable. It provides out of the box:
- Multi-currency double-entry accounting using only a plain text file
- Assisted data entry or CSV import
- Zero-setup command line, terminal, and web user interfaces
- Multi-period balance sheet, income statement, and cashflow reports
- Summarising of account balances to a desired depth
- Output to text, HTML, CSV, JSON or SQL
- Import/export/co-usage with Ledger CLI or Beancount
- Easy to download or build on all major platforms
Several UIs to choose from
hledger comes with several user interfaces that just work:
- Command line - a comfortable command line interface for full control
- Terminal UI - a live-updating interface for quickly reviewing balances and transactions (screencast)
- Web UI - an easy web interface for terminal-free browsing and data entry. Runs locally or on a server (demo)
- Haskell library - write scripts, add-on commands, or applications as powerful as hledger itself.
Fast
- Reports take a fraction of a second
hledger-ui --watchupdates instantly as you edit- Processes 15k-25k transactions/second (benchmark)
Dependable and robust
hledger strives to be absolutely dependable and never waste your time:
- Robust installation - Multiple binary and source options, reliable builds across platforms
- Robust execution - Written in Haskell, minimizing runtime failures through strong compile-time checking
- Robust features - Commands and options combine well, handling all kinds of input sensibly
- Robust parsing - Dated items processed in date order, multiple assertions work as expected
- Robust calculation - Results match manual calculations perfectly, up to 255 decimal places
- Robust reporting - Deterministic reports, unaffected by input file order
- Robust testing - Continually tested by 1000+ automated tests
- Robust documentation - All functionality documented precisely with mnemonic permalinks, built-in offline manuals
Cross platform
hledger works well on Windows, Mac, and Unix systems, with official binaries provided for each. There is also a mobile app.
Compatible
hledger was inspired by Ledger CLI and provides most of its features, in more depth and with fewer sharp edges. Ledger users will find the file formats and commands familiar, and can often run both tools on the same data files (more about origins and differences).
hledger can convert to/from Beancount format. Many import tools exist for other applications. Data exports to CSV, JSON, or SQL.
Free Software
Free Software with no purchase price or monthly fees, licensed under GNU GPLv3+. You always have the right to run, inspect, modify, or share it.
Actively maintained and supported
hledger is actively maintained, with regular releases and strong community support.
When might hledger not be a good choice ?
hledger is not yet ..
- easy for completely non-technical people to install and use without help
- easy to use on a phone
- equipped with mature, polished GUIs, or easy charts
- able to download directly from banks and financial institutions
- capable of automatically tracking capital gains like Beancount and Ledger
- masterable in a day (or a week..)
Plain Text Accounting, and Double Entry Bookkeeping and Accounting generally, are deep and rich topics. As a newcomer you might feel there’s both too much and not enough to read; too much flexibility, and not enough structure or guidance.
You may need to pace yourself, ask the chat for tips, and learn through hands-on practice.
Forecasting
Some ways:
-
Enter future-dated transactions in your journal, commented out (with
;orcomment) -
Enter future transactions uncommented; use a query to exclude them from reports when needed (
-e tomorrowordate:-tomorrow. hledger-ui hides them by default.) -
Enter future transactions in a separate
forecast.journal, which you can include when needed (eg, add-f forecast.journal). -
Enter periodic transaction rules describing future transactions (recurring or non-recurring), and activate the forecast with
--auto. -
Budgeting and forecasting (2018) > Forecasting - reusing a budget’s periodic transactions to generate a forecast.
Foreign trip expenses
From https://www.reddit.com/r/plaintextaccounting/comments/9r9cfj/beancount_price_and_cost :
- Before going to vacation to Europe, I borrowed 350 EUR, cash.
- I also took out of ATM 200 EUR, cash - now I know the price.
- I spent 500 EUR in trip, and I have 50 left.
- Now, after the trip, I exchanged some of my home currency to 300 EUR to give it back - and it’s the different price from step two. So how do I write all this down?
My attempt follows. Notes:
- When transactions occur on such trips, I sometimes know the USD amount spent, and sometimes the EUR amount. I sometimes know the total converted amount, and sometimes the conversion rate. I record whichever of these is more convenient.
- After the trip, when reviewing expenses, I’ll add a P market price directive covering the period of the trip, and use -V to see all expenses in home currency (USD).
; a hledger example based on colindean's
; hledger doesn't currently support the {} syntax, just @ or @@
2018-10-25 * vacation loan
Liabilities:Loans:Vacation
Assets:Cash 350 EUR
2018-10-26 * ATM withdrawal
Assets:Cash 200 EUR @@ 220 USD ; conversion price written out for clarity; redundant due to -225 USD below
Expenses:Fees:CurrencyConversion 5 USD
Assets:Bank -225 USD
2018-10-27 * food
Assets:Cash -190 EUR
Expenses:Vacation:Food
2018-10-27 * hotel
Assets:Cash -310 EUR = 50 EUR ; assert that Cash's EUR balance is now 50
Expenses:Vacation:Hotel
2018-10-28 * withdraw more euros to repay loan
Assets:Cash 300 EUR @@ 360 USD ; conversion rate has gone up to 1.20
Expenses:Fees:CurrencyConversion 5 USD
Assets:Bank -365 USD
2018-10-28 * repay vacation loan
Liabilities:Loans:Vacation 350 EUR = 0 EUR ; assert that euro loan is repaid
Assets:Cash
; Conversion rate to use in reports for the trip period.
; You could declare each time it changed, eg:
; P 2018-10-25 EUR 1.10 USD
; P 2018-10-28 EUR 1.20 USD
; but hledger currently picks just one,
; and for expense reporting a rough average price is usually fine:
P 2018-10-25 EUR 1.15 USD
Here are a few different reports, for comparison:
Simple balance change report for all accounts. –flat and -Y help ensure a readable tabular layout here.
$ hledger bal --flat -Y
Balance changes in 2018:
|| 2018
==================================++======================
Assets:Bank || -590.00 USD
Expenses:Fees:CurrencyConversion || 10.00 USD
Expenses:Vacation:Food || 190 EUR
Expenses:Vacation:Hotel || 310 EUR
----------------------------------++----------------------
|| 500 EUR, -580.00 USD
Adding the -B/–cost flag converts transaction amounts to the other commodity in the transaction, using the conversion rate specified in the transaction if any. This typically helps collapse the grand total to one commodity, so we can see it is zero here (expected, since we’re showing all accounts).
$ hledger bal --flat -Y -B
Balance changes in 2018:
|| 2018
==================================++======================
Assets:Bank || -590.00 USD
Assets:Cash || -500 EUR, 580.00 USD
Expenses:Fees:CurrencyConversion || 10.00 USD
Expenses:Vacation:Food || 190 EUR
Expenses:Vacation:Hotel || 310 EUR
----------------------------------++----------------------
|| 0
Adding the -V/–value flag instead converts report amounts using the market price effective on the reporting date (hledger prices and date can help identify that). The grand total of -5 USD here corresponds to our capital loss due to change in exchange rate (the price of a euro went from $1.10 to $1.20 while we still owed some):
$ hledger prices
P 2018-10-25 EUR 1.15 USD
$ date
Fri Oct 26 15:03:00 PDT 2018
$ hledger bal --flat -Y -V
Balance changes in 2018:
|| 2018
==================================++=============
Assets:Bank || -590.00 USD
Expenses:Fees:CurrencyConversion || 10.00 USD
Expenses:Vacation:Food || 218.50 USD
Expenses:Vacation:Hotel || 356.50 USD
----------------------------------++-------------
|| -5.00 USD
The “exp” account query is added to show just the expenses. Now we can see their total.
$ hledger bal --flat -Y -V exp
Balance changes in 2018:
|| 2018
==================================++============
Expenses:Fees:CurrencyConversion || 10.00 USD
Expenses:Vacation:Food || 218.50 USD
Expenses:Vacation:Hotel || 356.50 USD
----------------------------------++------------
|| 585.00 USD
Or you might use the is/incomestatement command which is specialised for income/expense reporting. It’s tabular and flat by default.
$ hledger is -V
Income Statement 2018/10/25-2018/10/28
|| 2018/10/25-2018/10/28
==================================++=======================
Revenues ||
----------------------------------++-----------------------
----------------------------------++-----------------------
||
==================================++=======================
Expenses ||
----------------------------------++-----------------------
Expenses:Fees:CurrencyConversion || 10.00 USD
Expenses:Vacation:Food || 218.50 USD
Expenses:Vacation:Hotel || 356.50 USD
----------------------------------++-----------------------
|| 585.00 USD
==================================++=======================
Net: || -585.00 USD
Getting Prices
Here are some ways to download current or historical market prices for your commodities.
pricehist
pricehist is a standalone tool for fetching currency, cryptocurrency and stock prices. It can print h/ledger or beancount-style price directives directly.
hledger-pricehist
is an optional small script to make it show up in the hledger commands list.
Install:
pipx install pricehist
List providers:
$ pricehist sources
alphavantage Alpha Vantage
bankofcanada Bank of Canada
coinbasepro Coinbase Pro
coindeskbpi CoinDesk Bitcoin Price Index
coinmarketcap CoinMarketCap
ecb European Central Bank
yahoo Yahoo! Finance
Show details about a provider’s data:
$ pricehist source yahoo
Fetch prices. The format of the symbol argument varies by provider and commodity.
$ pricehist fetch -o ledger -s 2025-12-19 -e 2025-12-19 ecb EUR/USD
P 2025-12-19 00:00:00 EUR 1.1712 USD
$ pricehist fetch -o ledger -s 2025-12-19 -e 2025-12-19 yahoo TSLA
P 2025-12-19 00:00:00 TSLA 478.975006103515625 USD
$ pricehist fetch -o ledger -s 2025-12-19 -e 2025-12-19 yahoo ADA-USD
P 2025-12-19 00:00:00 ADA-USD 0.37601006031036376953125 USD
You can omit the start or end options to fetch a range of dates. Some commodities will not have prices available on weekends. Some providers (eg alphavantage) may require signup and an API key to get certain data.
bittytax_price
bittytax_price, from the BittyTax project, can fetch currency and cryptocurrency prices (but not stock prices). It’s quite easy to use, trying multiple providers automatically.
Install:
UK version:
git clone https://github.com/BittyTax/BittyTax.git
cd BittyTax && pip install .
or US version:
git clone https://github.com/BittyTax/BittyTax.git -b features/usa-tax-rules
cd BittyTax && pip install .
Run it once to set up a config file (~/.bittytax/bittytax.conf):
$ bittytax_price --version
Configure your preferred base currency in the config file:
local_currency: 'USD'
Fetch prices:
$ bittytax_price latest EUR
1 EUR=1.1712 USD via BittyTaxAPI (Euro)
1 EUR=$1.17 USD
$ bittytax_price latest BTC
1 BTC=87,953.36 USD via CryptoCompare (Bitcoin)
1 BTC=$87,953.36 USD
If you get Too Many Requests errors from Coingecko,
you can remove them from data_source_crypto in the config file,
or buy an API key,
or specify a source with the -ds command line option.
hledger and Beancount
Tips for co-using or converting between Beancount and hledger. If you are a Beancount user trying to use your data with hledger, or vice-versa, please let me know your experience so I can make this smoother.
See also:
Differences
For some project and feature differences, see the PTA apps overview and Feature matrix.
hledger focusses strongly on UX, reliability, and real-world practicality. It aims to be somewhat useful to non-techies and accounting beginners, as well as experts. It is written in Haskell, which helps with correctness and maintainability.
Compared to hledger, Beancount has
- a more active mail list
- a better web app (Fava)
- more automated lot tracking for investment transactions
- a data format extendable by plugins
- a Python API
Compared to Beancount, hledger has
- more active chat rooms
- more active maintenance and support. There’s a new release every quarter.
- good installability (a clear best version, a single binary to install, easy install on Windows)
- more speed
- up-to-date docs
- more beginner resources
- a single reference manual, viewable in multiple formats and offline
- easier, built in features: data entry, CSV import, reports, query syntax, multi-period reports, time logging
- multiple officially-supported user interfaces (CLI, TUI, web, HTTP-JSON), and export to Fava
- fully documented future-proof data formats
- a Haskell API
Beancount’s journal syntax is similar to hledger’s, but different enough that neither can read the other’s files directly.
hledger to Beancount
The best way to export from hledger to Beancount is with hledger’s print command, which supports Beancount output.
This can be useful for viewing hledger data in Fava, eg. In simple cases it can be:
$ pip3 install fava
$ hledger print -o tmp.beancount
$ fava tmp.beancount
Beancount to hledger
The most reliable way to export Beancount data is with Beancount 2’s bean-report tool. It’s not available with Beancount 3, you’ll need to install Beancount 2:
$ pip3 install beancount==2.3.6
Then you can do
$ bean-report example.beancount hledger > example.hledger
This is a good start, but not yet robust. Here are some things you may need to fix manually, as of 2.3.6.
Total costs
bean-report converts @@ total costs to @ unit costs.
@ costs are more standard and often more useful, eg when selling part of a lot.
But this conversion tends to create imprecise entries, causing hledger to complain that transactions are unbalanced.
You may need to manually adjust the decimals in such entries.
(Or add an amountless expenses:rounding posting.)
Double @
bean-report converts the combination of {} and @ to invalid syntax with two @’s.
Eg:
2013-10-21 * "Sell shares of ITOT"
Assets:US:ETrade:ITOT -8 ITOT {101.20 USD} @ 105.75 USD
Assets:US:ETrade:Cash 837.05 USD
Expenses:Financial:Commissions 8.95 USD
Income:US:ETrade:Gains -36.40 USD
becomes
2013-10-21 * Sell shares of ITOT
Assets:US:ETrade:ITOT -8 ITOT @ 101.20 USD @ 105.75 USD
Assets:US:ETrade:Cash 837.05 USD
Expenses:Financial:Commissions 8.95 USD
Income:US:ETrade:Gains -36.40 USD
Quick fix: comment out the second @ price:
Assets:US:ETrade:ITOT -8 ITOT @ 101.20 USD ;@ 105.75 USD
Queries
Custom query definitions are not properly commented out:
2015-01-01 query "home" "
SELECT LAST(date) as latest, account, SUM(position) as total
WHERE account ~ ':Home:'
GROUP BY account
"
becomes
;; Query: 2015-01-01 "home" "
SELECT LAST(date) as latest, account, SUM(position) as total
WHERE account ~ ':Home:'
GROUP BY account
"
Quick fix: fully comment out any query definitions:
;; Query: 2015-01-01 "home" "
; SELECT LAST(date) as latest, account, SUM(position) as total
; WHERE account ~ ':Home:'
; GROUP BY account
;"
So, something like this (applies the quick fixes above) might work better for you:
$ bean-report example.beancount hledger | perl -pe 's/(@.*?)@/\1;@/' | perl -p0e 's/^(;; Query.*?^")/comment\n\1\nend comment/smg' > example.hledger
or just run a single report without saving:
$ bean-report example.beancount hledger | perl -pe 's/(@.*?)@/\1;@/' | perl -p0e 's/^(;; Query.*?^")/comment\n\1\nend comment/smg' | hledger -f- stats
hledger and dsq/DataStation
https://github.com/multiprocessio/datastation GUI for reporting on CSV and other tabular file formats
https://github.com/multiprocessio/dsq command line spinoff
dsq makes CSV and some other things queryable with SQL, as if it was an sqlite database. (It is powered by sqlite, with additional functions from the go sqlite lib). So combined with CSV output from hledger print or hledger register it’s an instant new sqlite-like query language. Some examples with dsq 0.20.1:
A quick one-line query on postings:
hledger print -O csv | dsq -s csv -n -p "select * from {} where date>'2022-06-15' and abs(amount) > 500"
-n interprets numeric fields more accurately as numbers. -p shows pretty text output (otherwise it shows json and runs faster).
If reading one file, you can enable caching with -C, giving a speedup when the file has not changed:
hledger print -o 2022.csv
time dsq 2022.csv -C -n -p "select count(*) from {}"
time dsq 2022.csv -C -n -p "select count(*) from {}"
-i gives an interactive REPL, avoiding reparsing files between commands, for another speedup:
$ dsq 2022.csv -C -n -p -i
dsq> select count(distinct date) from {}
+----------------------+
| count(distinct date) |
+----------------------+
| 130 |
+----------------------+
(1 row)
dsq> select description,amount from {} order by date desc limit 1
+--------+-------------+
| amount | description |
+--------+-------------+
| 3.5 | chipper |
+--------+-------------+
(1 row)
dsq>
Fields are sorted alphabetically, for now.
The CLI has a few issues you might run into - combining flags, multiple files, cache initialisation, crashing on empty results…
With no query, dsq outputs JSON (and piping through jq will prettify it).
So here’s a way to export transactions as human-readable JSON
that is simpler than hledger’s -O json output:
$ hledger print -O csv | dsq -s csv -n | jq -S
[
{
"date2": "",
"amount": -1,
"txnidx": 1,
"credit": 1,
"description": "income",
"status": "",
"code": "",
"account": "income:salary",
"date": "2008-01-01",
"commodity": "$",
"debit": "",
"posting-status": "",
"posting-comment": "",
"comment": ""
},
...
hledger and GnuCash
Tips for co-using/converting/switching GnuCash and hledger.
Conversion tools. gnucash-xml, gnucash-sql, gnucash-sqlite indicate which GnuCash storage back ends are supported: XML, general SQL (sqlite/mysql/postgres), or sqlite only:
- WolfgangFahl/pynomina (updated 2025) converts between many formats, including gnucash-xml and beancount
- piecash/scripts/ledger.py (updated 2024) gnucash-sql to ledger
- lodenrogue/gnucash2ledger (2023) gnucash-xml to ledger
- tfree87/gnucash2ledger (2021) gnucash-xml to *ledger
- gnucash-xml-to-ledger-dat (2020) gnucash-xml to ledger
- gnucash-to-beancount (2017) gnucash-sqlite to beancount
- cookthebooks (2017) gnucash-xml to ledger
- pygnucash/gnucash2ledger.py (2014) gnucash-sqlite to ledger
Some related discussion:
- Import gnucash Files #332
- reddit: command line vs GUI (2023) how to export from hledger to gnucash
- HN: GnuCash Tutorial and Concepts Guide (2023) someone using gnucash and hledger together
- reddit: Current working gnucash exporter? (2021)
- reddit: GnuCash and ledger combination (2021)
- HN: Simple Personal Finance Tracking with GnuCash (2020) gnucash vs ledger
- Converting GNUCash’s XML file to Ledger’s DAT format (2020)
- Scripting with APL: Convert GNUCash XML format to Ledger CLI (2016) (apl)
hledger and just
https://github.com/casey/just is a cross platform task runner - like make, but easier and more suitable for running commands (and more cross platform). It is a nice tool for creating (and remembering!) financial reports and scripts.
Define commands (AKA “recipes”) in a Justfile in your finance directory.
Each recipe can be a single command line, a shell script, or any other kind of shebang script.
Here’s just’s manual.
We provide an example Scripts > Justfile,
but of course you can customise this with your own reports
or start fresh with just --init.
Then you can
$ just -lu # list your commands, with descriptions, in original order
$ just CMD [ARGS] # run a command, optionally with extra arguments
$ just --choose # pick a command interactively
$ alias j=just # use an easier-to-type alias
Our justfile’s commands pass any extra arguments to hledger, but this does not work for arguments containing spaces, so avoid those:
$ just x food -p 'last month' # bad
$ just x food -plastmonth # good
Interactive choosing
just --choose will let you choose a command interactively,
by default using fzf and showing a preview of the system command(s) that will be run.
You can configure a different chooser with –chooser or JUST_CHOOSER (Just > Selecting Recipes to Run With an Interactive Chooser). Eg on mac, this shows a GUI chooser:
$ brew install choose-gui
$ export JUST_CHOOSER=choose
$ just --choose
Showing report output in the preview pane
This is great for selecting and viewing reports. Careful - it will run every command you select! So be sure your commands are safe and reasonably quick to run (eg hledger reports). Then:
$ export JUST_CHOOSER="fzf --preview='just {}'"
$ just --choose
With bkt, you can cache these outputs, making slow reports display instantly (here’s more on –ttl and –stale and using bkt with fzf):
$ export JUST_CHOOSER="fzf --preview='bkt --ttl=15m --stale=15s -- just {}'"
$ just --choose
just view
The example justfile has a view command with the above configuration built in.
It uses fzf and bkt (and ignores JUST_CHOOSER).
It takes one dummy argument, and optional additional fzf arguments:
$ just view -
$ just view - --black
There is also a pick command which does not run commands while selecting.
It uses JUST_CHOOSER, or fzf with the command source as preview.
view uses fzf’s –reverse flag; you could make pick do the same like so:
$ export JUST_CHOOSER="fzf --preview='-- just --show {}' --reverse"
Excluding commands from the chooser
If you want preview the output of some commands but not of others (eg, because they have side effects), you could move those commands to a separate just file.
Or, you can make sure they are declared with a required argument,
adding a dummy argument if necessary (as in the example file).
Commands with required arguments are always excluded from the chooser.
(Note this excludes them from the chooser entirely, not just from previewing,
but they can be run from the command line, writing - for the dummy argument.)
hledger and Ledger
hledger was inspired by the app that pioneered plain text accounting: Ledger (https://ledger-cli.org). This page describes differences between them, and a little history.
If you are a Ledger user trying to use your data with hledger, or vice-versa, please let me know your experience so I can make this smoother. Interoperating tips has specific tips.
See also:
- Which PTA app should I choose ? (PTA FAQ)
- ledger vs hledger (reddit, 2022)
- easy Ledger file reading (#1962)
Differences
hledger focusses strongly on UX, reliability, and real-world practicality. It aims to be somewhat useful to non-techies and accounting beginners, as well as experts. It is written in Haskell, which helps with correctness and maintainability. It tries to reimplement Ledger’s best parts in more depth, with more consistency and robustness.
Compared to Ledger, hledger has
- more active maintenance and support (since 2008). There’s a new release every quarter.
- a complete and accurate manual
- more built in reports, including standard financial reports
- multi-period reports
- easier query syntax
- better depth limiting
- a battle-tested CSV/SSV/TSV import system
- multiple officially-supported user interfaces: CLI, TUI, web, HTTP-JSON
- timedot time logging format
- fully documented future-proof declarative data formats
- a Haskell API.
Compared to hledger, Ledger has
- assisted lot tracking for investment transactions
- a data format supporting embedded code (value expressions, python expressions..)
- more speed with large files
- a C++ API.
hledger can read Ledger’s files, and vice versa, if you avoid using tool-specific syntax.
Feature differences
Over time, features have propagated both ways. These features are common to both hledger and Ledger:
- command line interface
- journal, timeclock, csv input formats
- csv conversion rules
- text, csv output formats
- multiple commodities
- costs and cost reporting
- market prices and value reporting
- virtual/unbalanced postings
- automated postings
- periodic transactions
- budget reporting
- unrealised capital gains reporting
- report filtering with flags and query arguments
- basic output format customisation
- commands: accounts, balance, commodities, payees, prices, print, register, stats, tags
For differences, see the detailed PTA Feature Matrix.
The Why hledger ? page introduces hledger’s features for new users.
Performance differences
Ledger was traditionally faster than hledger with large files, eg above 5k transactions. (Many people record about 1-2k transactions per year.) Ledger’s speed came partly from providing fewer guarantees, eg Ledger’s balance assertions/assignments are not date-aware.
Since about 2021 the performance gap seems to me to have closed or reversed, at least on my mac, where hledger often runs faster and in less memory than Ledger, especially with very large files.
In 2022, hledger ~1.25 compiled natively on a macbook air m1 processed 25k transactions per second:
$ hledger --version
hledger 1.24.99.2-gba5b0e93f-20220205, mac-aarch64
$ make throughput
date: Tue Feb 8 11:03:50 HST 2022
system: Darwin slate.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_ARM64_T8101 arm64
executable: hledger
version: hledger 1.24.99.2-gba5b0e93f-20220205, mac-aarch64
1000 txns: Run time (throughput) : 0.07s (15308 txns/s)
2000 txns: Run time (throughput) : 0.09s (21121 txns/s)
3000 txns: Run time (throughput) : 0.13s (23648 txns/s)
4000 txns: Run time (throughput) : 0.17s (23226 txns/s)
5000 txns: Run time (throughput) : 0.21s (23647 txns/s)
6000 txns: Run time (throughput) : 0.24s (24784 txns/s)
7000 txns: Run time (throughput) : 0.29s (24166 txns/s)
8000 txns: Run time (throughput) : 0.33s (24450 txns/s)
9000 txns: Run time (throughput) : 0.35s (25516 txns/s)
10000 txns: Run time (throughput) : 0.41s (24226 txns/s)
100000 txns: Run time (throughput) : 4.32s (23158 txns/s)
Tue Feb 8 11:03:57 HST 2022
Newer hledger versions are slower than this. hledger 1.29-1.32.2 have a performance bug which can be seen with large files, #2153 (see eg 2153#issuecomment-1912942305 benchmarks).
2024’s hledger 1.40 on macbook air m1 runs at roughly 16k txns/s for me:
$ hledger -f examples/100ktxns-1kaccts.journal stats
Main file : .../100ktxns-1kaccts.journal
Included files : 0
Txns span : 2000-01-01 to 2273-10-16 (100000 days)
Last txn : 2273-10-15 (90965 days from now)
Txns : 100000 (1.0 per day)
Txns last 30 days : 31 (1.0 per day)
Txns last 7 days : 8 (1.1 per day)
Payees/descriptions : 100000
Accounts : 1000 (depth 10)
Commodities : 26
Market prices : 100000
Runtime stats : 6.23 s elapsed, 16051 txns/s, 258 MB live, 773 MB alloc
More independent benchmarking is needed, help welcome.
Command line differences
-
hledger does not require a space between command-line flags and their values, eg
-fFILEworks as well as-f FILE -
hledger uses
--ignore-assertions/-Ito disable balance assertions. Ledger uses--permissivefor that, and uses-Ias the short form of--prices. -
hledger’s
-x/--explicitflag makes print show all amounts; Ledger’s--explicitflag does something else. -
hledger’s and Ledger’s
-H/--historicalflags are unrelated. hledger’s-Hmakes register and balance-like commands include balances from before the report start date, instead of starting at zero:hledger register --help: -H --historical show historical running total/balance (includes postings before report start date) hledger balance --help: -H --historical show historical ending balance in each period (includes postings before report start date)Whereas Ledger’s
-Hchanges the valuation date used by-V/-X:ledger --help: --historical (-H) Value commodities at the time of their acquisition. -
hledger’s query language is a little less powerful than Ledger’s, simpler, and easier to remember. It uses google-like prefixes, such as
desc:,payee:,amt:, andnot:. Multiple query terms are combined using fixed AND/OR rules. More complex query combinations with AND/OR/NOT can be expressed withexpr:"BOOLEANEXPR". Some complex queries can also be achieved by using two invocations of hledger in a pipe, eg:hledger print QUERY1 | hledger -f- -I reg QUERY2 -
hledger provides more short flags (
-b,-e,-p,-D,-W,-M,-Q,-Y) and thedate:query argument for setting report period and interval, and all of these combine nicely. -
hledger handles the transaction/posting status mark more precisely, fixing a recurring source of confusion: It calls the three statuses “unmarked”, “pending”, “cleared”, and provides corresponding
--unmarked/-U,--pending/-P, and--cleared/-Cflags, which can be combined. Ledger’s-U, which matches things not cleared, is-UPin hledger. hledger’s-Umatches things with no status mark, which can’t easily be done in Ledger.
Journal format differences
hledger’s journal format mimics (a subset of) Ledger’s quite closely. You can maintain a journal file that works with both hledger and Ledger simultaneously, if you restrict yourself to compatible features, perhaps keeping non-compatible features in separate files.
A typical gnarly old Ledger file will not work with hledger as-is. Here are some of the roadbumps to expect (see also: #1752):
- Some features are supported only by one or the other (Ledger’s
(AMOUNTEXPR),((VALUEEXPR)).. , hledger’s==,=*,==*..) - Some will be accepted but ignored, probably causing transactions not to balance (
{LOTCOST},{=LOTFIXEDCOST},[LOTDATE],(LOTNOTE)..) - Some may be interpreted differently (balance assertions, balance assignments..)
- Some may have different restrictions (dates, comments..)
You’ll find lots of tips for how to handle these and other differences, below.
But first, an overview of Ledger’s (extensive) journal format. Here are its features, from the Ledger manual (2022-12), and their supportedness in hledger (1.40, 2024-09): Y (supported), Ignored (accepted but ignored), or N (not accepted).
| Supported in hledger ? | 1.40 | hledger name, notes |
|---|---|---|
| TRANSACTIONS SYNTAX | ||
| 5.1 Basic format | Y | |
| 5.2 Eliding amounts | Y | “Inferred / implicit amounts” |
| 5.3 Auxiliary dates | Y | “Secondary dates”. A misfeature, best avoided. |
| 5.4 Codes | Y | |
| 5.5 Transaction state | Y | “Transaction status” |
| 5.6 Transaction notes | Y | “Transaction description (and/or payee)” |
| 5.7 Metadata | Y | “Tags” |
| 5.7.1 Metadata tags | Y | “Tag names”. Format is TAG1:, TAG2: not :TAG1:TAG2:. |
| 5.7.1.1 Payee metadata tag | N | |
| 5.7.2 Metadata values | Y | “Tag values”. They are terminated by a comma or newline. |
| 5.7.3 Typed metadata | N | Values of the date:/date2: tags are checked as dates; all others are strings. |
| 5.8 Virtual postings | Y | “Virtual postings” AKA “unbalanced postings”, and “balanced virtual postings” |
| 5.9 Expression amounts | N | |
| 4.5.4 Value expressions | N | |
| 5.10 Balance verification | Y | |
| 5.10.1 Balance assertions | Y | |
| 5.10.1.1 Special assertion value 0 | N | |
| 5.10.2 Balance assignments | Y | |
| 5.10.3 Resetting a balance | Y | |
| 5.10.4 Balancing transactions | Y | |
| 5.11 Posting cost | Y | “Inferred cost” |
| 5.12 Explicit posting costs | Y | “(Explicit) unit cost” |
| 5.12.1 Primary and secondary commodities | Y | Essentially yes - in certain cases, order of postings determines which commodity is converted to. |
| 5.13 Posting cost expressions | N | |
| 5.14 Total posting costs | Y | “(Explicit) total cost” |
| 5.15 Virtual posting costs | Ignored | The parentheses are ignored (it’s treated like a regular cost). |
| 5.16 Commodity prices | Ignored | Lot costs are recorded in the transaction, but not carried along with the lot automatically, and {AMT} is ignored. Such Ledger entries probably won’t balance. |
| 5.16.1 Total commodity prices | Ignored | |
| 5.17 Prices versus costs | N | If attached to a posting amount we call it “cost”, if declared ambiently with a P directive we call it “price” or “market price”. {AMT} is ignored. |
| 5.18 Fixated prices and costs | Ignored | {=AMT} is ignored. |
| 5.19 Lot dates | Ignored | |
| 5.20 Lot notes | Ignored | |
| 5.21 Lot value expressions | N | |
| 5.22 Automated Transactions | Y | “Auto postings” |
| 5.22.1 Amount multipliers | Y | We use a different syntax (*NUM or *AMT). |
| 5.22.2 Accessing the matching posting’s amount | N | |
| 5.22.3 Referring to the matching posting’s account | N | |
| 5.22.4 Applying metadata to every matched posting | Ignored | Tags attached to an auto posting rule’s “transaction line” are ignored. |
| 5.22.5 Applying metadata to the generated posting | Y | Tags attached to an auto posting rule’s postings affect the generated postings. |
| 5.22.6 State flags | Y | “Posting status” of auto posting rule’s postings affect the generated postings. |
| 5.22.7 Effective Dates | Y | “Secondary dates”, see above. |
| 5.22.8 Periodic Transactions | Y | |
| DIRECTIVES doc | ||
P historical (market) prices | Y | “Market price”, “price” |
= An automated transaction. | Y | “Auto posting rule” |
~ A periodic transaction. | Y | “Periodic transaction rule” |
; # % * | comment lines | Y | “Comment line”. % and | are not supported. |
! or @ as a directive prefix | Y | Legacy syntax, best avoided. |
account pre-declare account names | Y | |
account subdirectives | Ignored | |
apply account set a default parent account | Y | |
apply fixed set fixated prices | Ignored | |
apply tag assign a tag to transactions | Ignored | |
alias rewrite account names | Y | “Account alias” (basic or regular expression) |
assert error if a value expression fails | Ignored | Use hledger check assertions or hledger-check-fancyassertions instead. |
bucket/A set a default balancing account | Ignored | |
capture replace accounts matched by regex with another | Ignored | Can be emulated with a regular expression account alias. |
check warn if a value expression fails | Ignored | Use hledger check assertions or hledger-check-fancyassertions. |
comment start multi-line comments | Y | “Multi-line comment”, “Comment block” |
commodity pre-declare commodities | Y | |
commodity subdirectives | Y | format is supported, other subdirectives are ignored. |
define define value expressions for future use | Ignored | |
end/end apply shorthand for ending most recent apply block | N | |
end apply account | Y | |
end apply fixed | Ignored | |
end apply tag | Ignored | |
end apply year | Ignored | |
end tag | Ignored | |
eval/expr evaluate a value expression | Ignored | |
include include another file | Y | |
payee pre-declare payee names | Y | |
payee subdirectives | Ignored | |
python embed python in journal | Ignored | |
tag pre-declare tag names | Y | |
tag subdirectives | Ignored | |
test, a synonym for comment | N | |
value EXPR set a default valuation function for all commodities | Ignored | |
Y/year/apply year set the year for year-less dates | Y | |
N COMM ignore pricing information for a commodity | Ignored | |
D AMT set a default commodity and its format | Y | A decimal mark is required (followed by 0 or more zeroes). |
C AMT1 = AMT2 declare a commodity equivalency | Ignored | |
I, i, O, o, b, h timeclock entries in journal | N | Timeclock data must be in a separate timeclock file. (A journal file can include that if needed.) |
--command-line-flags in journal | Ignored |
Decoding errors
hledger reports an error if it sees non-ascii data which can’t be decoded using the system locale’s text encoding. To avoid this, see Install: Text encoding.
Tabs and spaces
In places which normally require two or more spaces (or tabs), eg between account name and amount, ledger will also accept a single tab character. But hledger always requires two or more spaces or tabs (ensuring a visually distinct gap). So you might need to add a space in such cases.
Decimal mark
Ledger parses 1,000 as 1000, but hledger parses it as 1, by default
(see hledger > Decimal marks).
To prevent any undetected disagreements, use commodity directives or decimal-mark directives to disambiguate the decimal mark character during parsing.
Balancing precision
Ledger and hledger <1.50 could occasionally disagree on whether a transaction’s journal entry is balanced, such as in this case:
2022-01-01 txn1
expenses AAA 989.02 @ $1.123456 ; $1111.12045312
checking $-1111.12
2022-01-02 txn2
expenses $0.1234
checking
Ledger checks txn1’s $ balance with the entry’s local $ precision, 2 decimal places; so its $-0.00045312 imbalance is accepted.
hledger <1.50 checked it with the journal’s global $ precision, 4 decimal places; so that imbalance was rejected,
requiring a -c '\$0.00' option or commodity $0.00 directive to make things work.
To avoid this problem, use hledger 1.50+, which balances transactions as Ledger does.
Balance assertions / assignments
Ledger calculates balance assignments and checks balance assertions simply in the order they were parsed. hledger calculates balance assignments and checks balance assertions in date order and then (for postings on the same date) parse order. This ensures correct, reliable behaviour independent of the ordering of journal entries and files.
hledger correctly handles multiple balance assignments/assertions within a single transaction.
Ledger rejects the following balance assertion, as if (a) and a were different accounts; hledger does not.
2023-01-01
(a) 1
2023-01-02
a 1 = 2
b
In addition to =, hledger supports several other kinds of balance assertion, with syntax ==, =* and ==*. Ledger rejects these.
hledger allows @/@@ cost notation in balance assertion/assignment amounts,
ie to the right of the equals sign; Ledger does not.
hledger adds a restriction on balance assignments: it does not allow balance assignments on accounts affected by auto posting rules (since in general this can make balancing the journal impossible).
Directive scope
The region affected by directives, and their behaviour with included files or sibling files, is sometimes different in hledger. This is to ensure robust, predictable behaviour. Here are hledger’s Directive effects and Directives and multiple files behaviour. You might need to move directives and/or rearrange your files.
Commodity directives
hledger allows commodity directives with a format subdirective to be written as one line,
eg these are equivalent:
commodity JPY
format 1.00 JPY
commodity 1.00 JPY
hledger’s commodity directive currently ignores other subdirectives (eg alias).
hledger’s commodity directive always requires a decimal mark in the amount. To display no decimal digits, write the decimal mark at the end:
commodity 1000. JPY
And as mentioned above, hledger assumes a single period or comma is a decimal mark, so when specifying digit group marks, write a decimal mark as well: Eg:
commodity 1,000. JPY
See also: hledger > commodity directive.
Periodic transactions
hledger understands most Ledger periodic transactions, but if you find some variants that are not supported, please report.
When you do specify a custom start date, hledger will start the transactions on that date. Ledger seems to always generate them on the period boundaries.
Amount expressions
hledger does not support value expressions, Ledger’s embedded programming language.
In particular, parenthesised amount expressions like ($10 / 3) are not supported;
these must be converted to explicit amounts. Here are the known ways:
-
Convert each expression manually, eg replace
($10 / 3)with$3.333. -
Convert each expression with ledger. Eg in emacs, select the parenthesised expression and enter
C-u M-| xargs ledger eval(and remove the newline). This might not work in all cases. -
Convert all expressions with beancount. This is a lossy conversion, but it might be good enough. After installing ledger2beancount, beancount, and beancount2ledger (see #33), try:
$ ledger2beancount file.ledger > file.beancount $ beancount2ledger file.beancount > file.journal
Lot notation
hledger currently does not provide automatic lot selection or a --lots report;
instead you must track them manually, recording cost basis with @
and using explicit per-lot subaccounts and gain/loss postings
(see https://hledger.org/investments.html).
More importantly, hledger ignores Ledger’s lot notation, like -5 AAPL {$50.00} [2012/04/10] (Oh my!) @@ $375.00.
(Any of {LOTUNITCOST}, {{LOTTOTALCOST}}, {=FIXEDLOTUNITCOST}, {{=FIXEDLOTTOTALCOST}}, [LOTDATE], (LOTNOTE) after a posting amount).
This can disrupt transaction balancing, making files unreadable.
(#1084)
For now the only true workaround is to rewrite such entries to use hledger-style explicit lot notation.
Other differences
-
hledger’s input data formats (journal, timeclock, timedot, …) are separate; you can’t mix timeclock entries and journal entries in one file as in Ledger. (Though a journal file can
includea timeclock or timedot file.) This helps implement more useful error messages. -
hledger supports international number formats, auto-detecting the decimal mark (comma or period), digit group mark (period, comma, or space) and digit group sizes to use for each commodity. Or, these can be declared explicitly with commodity directives.
-
hledger’s default commodity directive (D) sets the commodity to be used for subsequent commodityless amounts, and also sets that commodity’s display settings if such an amount is the first seen. Ledger uses D only for commodity display settings and for the entry command.
-
hledger auto postings allow only minimal customisation of the amount (just multiplying the matched amount by a constant) - plus interpolation of matched text - but not a full embedded expression language like Ledger. (And we call them “auto” to avoid the spelling confusion of “automatic” vs “automated”.)
-
In multi-period reports, hledger expands the report start/end dates to span whole periods.
-
hledger’s print command always shows both the primary transaction date and any secondary date, in their usual positions. Ledger’s print command with
--aux-datereplaces the primary date with any secondary date. -
hledger always shows time balances (from timeclock or timedot data) in hours.
-
hledger always splits multi-day time sessions at midnight, to show the per-day amounts. Ledger does this only with the
--day-breakflag. -
hledger’s CSV/TSV/SSV-reading and import system is more mature and flexible than Ledger’s
convertcommand. -
Ledger can report multiple errors at once; hledger reports only one error at a time.
-
Ledger can also output warnings. hledger does not print warnings; it either succeeds or fails.
-
hledger will complain if transaction or posting comments contain
date:ordate2:not followed by a valid date tag value.
Interoperating tips
The core of hledger’s and Ledger’s journal formats is the same, so you can use both tools on the same files, if you are careful to avoid tool-specific features.
When you can’t avoid tool-specific syntax, you can put it in separate tool-specific files, and have both of these include a shared common file. (Eg 2023.ledger and 2023.hledger, both including 2023.journal).
A third approach is to do a one-way conversion to a new file, using whatever edits and transformations are necessary, and automate it as much as possible (with sed, perl, Emacs macros, or similar), so you can redo the conversion when needed, perhaps incrementally.
Ledger to hledger
Most Ledger users will have at least some Ledger-specific syntax, so the quickest way to tap into hledger reports may be:
$ ledger print | hledger -f- -I CMD
The print command omits directives.
-I disables checking of balance assertions (if needed).
(ledger’s --raw flag used to help hledger’s transaction balancing but probably isn’t needed since hledger 1.50.)
If this works you can do quick reporting like so:
$ ledger print | hledger -f- check # check for problems
$ ledger print | hledger -f- stats # show journal statistics
$ ledger print | hledger -f- is -MAS -2 # summarise monthly revenues/expenses
$ ledger print | hledger -f- web # view journal in hledger-web WUI
$ hledger-ui -f <(ledger print --raw) # view journal in hledger-ui TUI (works in bash)
Some common problems:
-
hledger does not support Ledger’s amount expressions, like
($10 / 3). If you have those, see Amount expressions above. -
hledger does not support all of Ledger’s lot notation, like
-5 AAPL {$50.00} [2012/04/10] (Oh my!) @@ $375.00. It can parse it, but will ignore it, so transaction balancing will probably fail. For now the only true workaround is to rewrite such entries to use hledger-style lot notation. See Lot notation above.
See also the other Differences mentioned above.
hledger to Ledger
Currently there’s no specific output format for Ledger; use print’s standard txt output format.
$ hledger print | ledger --permissive -f - CMD
Ledger requires a space between -f and -.
--permissive disables checking of balance assertions (if needed).
Some common problems:
-
hledger’s extended balance assertions (
=*,==,==*) are not supported by Ledger and must be avoided or commented out (eg withsed -E -e 's/(==|=\*)/; \1/'). -
hledger <1.50 sometimes rejected transactions which Ledger considers balanced, requiring additional configuration (see Balancing precision). Use hledger 1.50+ instead.
-
hledger print will add a trailing decimal mark to amounts with digit group marks and no decimal digits, to disambiguate them (since 1.31), but Ledger currently does not parse such numbers. You can avoid them by suppressing digit group marks (eg with
-c) or by ensuring some decimal digits (eg with--round); see hledger > Trailing decimal marks.
See also the other Differences mentioned above.
History
I (Simon) discovered John Wiegley’s Ledger in 2006, and was very happy to find this efficient command-line reporting tool with a transparent data format. Initially, I used it to generate time reports for my job. Before long I wanted some improvements - splitting sessions at day boundaries, reporting in hours, etc.
Meanwhile, John was now busy elsewhere. For a long time the Ledger project remained stalled, with unfixed functionality/documentation bugs and an ever-looming v3 release making life hard for new users and creating friction for community growth. I did what I could to help - reporting bugs, providing support, contributing a domain and website - but I didn’t want to invest in learning C++.
I was learning and investing time in Haskell, and I felt Ledger could be perhaps implemented well, and perhaps more effectively in the long run, in this language. I urgently needed a rock solid, hassle-free and enjoyable accounting tool. Also, I wanted a more active project and some way to make progress on the roadbumps and confusion facing other newcomers.
Of course I tried a little shiny-tech salesmanship on John, but couldn’t expect him to start over. (At that time he was deeply in the C++ world; nowadays he is a Haskell expert!)
So in 2007 I began experimenting. I built a toy parser in a few different languages, and it was easiest in Haskell. I kept tinkering. Goals included:
- to get better at Haskell by building something useful to me
- to implement at least the basic core of Ledger, adapted for my needs
- to learn how well Haskell could work for real-world applications
And later:
- to provide a new highly-compatible implementation of at least the basics of Ledger, useful to others, with a greater focus on ease of use, reliability, documentation and web presence
- to experiment with new user interfaces, APIs, etc.
Before too long I had a tool that was useful to me. With Ledger still installed, and by maintaining high compatibility, I now had two implementations which could be compared at times of confusion about functionality or suspected bugs/bookkeeping errors, which was quite valuable.
Later, John returned for a while and finished Ledger version 3, the Ledger project attracted new contributors and maintainers, and incremental improvements resumed. I continued sharing discoveries and design discussions, and we have seen many ideas propagating in both directions. I think having independent but compatible implementations has been quite helpful for troubleshooting, exploring the design space, and growing the community. For a while I ran LedgerTips on twitter.
hledger shared #ledger’s IRC channel until 2014, when I created the #hledger channel (now accessible on Libera IRC and Matrix).
In 2016 I set up https://plaintextaccounting.org as a common entry point and information hub.
The further adventures in hledger’s development are not yet told, other than in the commit log, issue tracker and mail list, but other contributors joined the project and CREDITS notes some of their work.
See also:
hledger and Obsidian
ledger-obsidian
- https://github.com/tgrosinger/ledger-obsidian
- https://www.reddit.com/r/ObsidianMD/comments/u9osib/anyone_out_there_using_the_ledger_plugin/
obsidian_hledger
- https://github.com/bzimor/obsidian_hledger
- https://www.reddit.com/r/ObsidianMD/comments/v6egmh/i_wrote_script_using_templater_to_add_hledger/
hledger and Paisa
https://github.com/ananthakumaran/paisa is a web app that reads a Ledger or hledger or Beancount file, downloads market prices, and provides sophisticated reports and charts. It is a slick, modern, easy-to-install app written in go and typescript and released under AGPL. It is focussed on Indian users but can be useful to others.
- https://ananthakumaran.in/paisa docs
- https://paisa-demo.ananthakumaran.in demo
- https://www.reddit.com/r/plaintextaccounting/comments/121ka8m/how_do_you_visualize_drill_down_your_financial related discussion
Paisa reads the journal file into its own SQLite database. You must have one of the PTA apps installed to create the SQLite database initially. After the database is created Paisa doesn’t strictly need the PTA app, but it is still useful to sync the latest changes from your journal to the db.
Paisa’s hledger support
Paisa added hledger support: see https://github.com/ananthakumaran/paisa/discussions/12. There are quite a few restrictions, which may or may not be documented, and we haven’t yet heard from anyone successfully using paisa with hledger. Please join the discussion and help with testing/improvements if you can.
Tips
Paisa will only see accounts whose top level is Assets, Liabilities, Equity, Income or Expense (case sensitive). If you don’t use those, you can use account aliases to rewrite them.
For other account names special to Paisa, see https://ananthakumaran.in/paisa/accounts.html.
hledger and Postgres
hledger2psql is a tool that exports a hledger journal to a postgres database.
hledger and Quicken/Quickbooks
Tips for co-using/converting/switching Quicken or Quickbooks and hledger.
Relevant data conversion tools include:
hledger and SimpleFIN
https://simplefin.org is a small-developer-friendly aggregator of US bank data - like Plaid or Yodlee, but simpler and cheaper. SimpleFIN works well as an import source for hledger.
Ideally your banks would run their own simplefin servers, but in practice you’ll be using SimpleFIN’s server. You authenticate directly with your banks, operations are read only, SimpleFIN apparently sees only encrypted data (you can optionally grant temporary unencrypted access for diagnosing problems).
An example workflow
Set up:
- Make a local copy of the bin/simplefinsetup,simplefinjson,simplefincsv scripts.
- Sign up with SimpleFIN, and copy the setup token.
- Paste the setup token into your
simplefinsetupscript, and run it to get a custom access url. - Paste the access url into your
simplefinjsonscript. Keep this url secret. - In a rules file for each bank account, configure a source rule to read that account’s data:
source | simplefincsv data/simplefin.json 'my bank name.*checking'
Every day or as needed:
- Download all accounts’ recent transactions:
simplefinjson >data/simplefin.json - Import new transactions to your journal (or see a preview):
hledger import *.rules [--dry-run]
See also
- https://github.com/simplefin/sfin2ledger an older converter (2016)
hledger and SQLite
With SQLite you can do full relational queries on your hledger data.
Here we export all transactions to a database and run some queries:
$ hledger -f examples/bcexample.hledger print -O sql | sqlite3 bcexample.db
$ sqlite3 bcexample.db -column -header
SQLite version 3.37.0 2021-12-09 01:34:53
Enter ".help" for usage hints.
sqlite> .tables
postings
sqlite> .schema
CREATE TABLE postings(id serial,txnidx int,date1 date,date2 date,status text,code text,description text,comment text,account text,amount numeric,commodity text,credit numeric,debit numeric,posting_status text,posting_comment text);
sqlite> select count(*) from postings;
count(*)
--------
3203
sqlite> select distinct txnidx,description,abs(amount) from postings order by abs(amount) desc limit 5;
txnidx description abs(amount)
------ ------------------------------------------------ -----------
1031 Allowed contributions for one year 17500
1035 Allowed contributions for one year 17500
1027 Allowed contributions for one year 17000
142 Transfering accumulated savings to other account 5000
954 Hoogle | Payroll 4615.38
sqlite> sqlite> select * from postings where txnidx=954;
id txnidx date1 date2 status code description comment account amount commodity credit debit posting_status posting_comment
-- ------ ---------- ----- ------ ---- ---------------- ------- ------------------------------------------ -------- --------- ------- ------- -------------- ---------------
954 2012-01-05 * Hoogle | Payroll Assets:US:BofA:Checking 1350.6 USD 1350.6
954 2012-01-05 * Hoogle | Payroll Assets:US:Vanguard:Cash 1200 USD 1200
954 2012-01-05 * Hoogle | Payroll Assets:US:Federal:PreTax401k -1200 IRAUSD 1200
954 2012-01-05 * Hoogle | Payroll Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200 IRAUSD 1200
954 2012-01-05 * Hoogle | Payroll Income:US:Hoogle:Salary -4615.38 USD 4615.38
954 2012-01-05 * Hoogle | Payroll Income:US:Hoogle:GroupTermLife -24.32 USD 24.32
954 2012-01-05 * Hoogle | Payroll Expenses:Health:Life:GroupTermLife 24.32 USD 24.32
954 2012-01-05 * Hoogle | Payroll Expenses:Health:Dental:Insurance 2.9 USD 2.9
954 2012-01-05 * Hoogle | Payroll Expenses:Health:Medical:Insurance 27.38 USD 27.38
954 2012-01-05 * Hoogle | Payroll Expenses:Health:Vision:Insurance 42.3 USD 42.3
954 2012-01-05 * Hoogle | Payroll Expenses:Taxes:Y2012:US:Medicare 106.62 USD 106.62
954 2012-01-05 * Hoogle | Payroll Expenses:Taxes:Y2012:US:Federal 1062.92 USD 1062.92
954 2012-01-05 * Hoogle | Payroll Expenses:Taxes:Y2012:US:State 365.08 USD 365.08
954 2012-01-05 * Hoogle | Payroll Expenses:Taxes:Y2012:US:CityNYC 174.92 USD 174.92
954 2012-01-05 * Hoogle | Payroll Expenses:Taxes:Y2012:US:SDI 1.12 USD 1.12
954 2012-01-05 * Hoogle | Payroll Expenses:Taxes:Y2012:US:SocSec 281.54 USD 281.54
954 2012-01-05 * Hoogle | Payroll Assets:US:Hoogle:Vacation 4.62 VACHR 4.62
954 2012-01-05 * Hoogle | Payroll Income:US:Hoogle:Vacation -4.62 VACHR 4.62
sqlite>
You can avoid creating a .db file, always querying the latest journal file instead:
$ (hledger print -O sql; echo "select * from postings where account like 'liabilities%' and amount > 0") | sqlite3 -column -header
Here’s a shell function to make that convenient:
$ hq() { (hledger print -O sql; echo "$1") | sqlite3 -column -header; }
Example queries:
$ hq "select distinct(account) from postings order by account"
$ hq "select * from postings where account like '%savings%' and amount > 0"
The txnidx field connects postings belonging to the same transaction.
Using this, we can query transactions, and in more complex ways than hledger can.
Here’s an example where we want to see just the transfers from Checking to Liabilities.
This is hard to do accurately with hledger’s CLI(1):
$ echo; hq "select * from postings where txnidx in \
(select txnidx from postings where account regexp 'Liabilities' and amount > 0 and txnidx in \
(select txnidx from postings where account regexp 'Checking' and amount < 0))"
> >
id txnidx date1 date2 status code description comment account amount commodity credit debit posting_status posting_comment
-- ------ ---------- ----- ------ ---- ------------------------------------ ------- --------------------------- ------- --------- ------ ------ -------------- ---------------
147 2012-01-08 * Chase:Slate | Paying off credit card Liabilities:US:Chase:Slate 140.36 USD 140.36
147 2012-01-08 * Chase:Slate | Paying off credit card Assets:US:BofA:Checking -140.36 USD 140.36
163 2012-02-11 * Chase:Slate | Paying off credit card Liabilities:US:Chase:Slate 725.96 USD 725.96
163 2012-02-11 * Chase:Slate | Paying off credit card Assets:US:BofA:Checking -725.96 USD 725.96
177 2012-03-09 * Chase:Slate | Paying off credit card Liabilities:US:Chase:Slate 580 USD 580
177 2012-03-09 * Chase:Slate | Paying off credit card Assets:US:BofA:Checking -580 USD 580
...
(1. Well.. it’s not that hard to get a decent result given typical data patterns:
$ hledger -f examples/bcexample.hledger print Checking | hledger -f- areg -w80 Liabilities amt:'>0'
Transactions in Liabilities and subaccounts:
2012-01-08 Chase:Slate | Pay.. Li:US:Ch:Slate 140.36 USD 140.36 USD
2012-02-11 Chase:Slate | Pay.. Li:US:Ch:Slate 725.96 USD 866.32 USD
2012-03-09 Chase:Slate | Pay.. Li:US:Ch:Slate 580.00 USD 1446.32 USD
...
but in general, this is not as robust as the relational query.)
Only hledger’s print command outputs SQL.
But many hledger reports can produce CSV, and you can also run SQLite on that:
or on hledger’s JSON output:
See also hledger and dsq / DataStation.
And Michael Peter: My plain text accounting workflow with hledger. This recommends the following export command to create a useful primary key:
hledger print -O sql | sed 's/id serial/id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL/g' | sqlite3 ledger.db
(#2017)
hledger and Ultorg
Ultorg provides a slick visual UI for relational data. It is an easy and powerful way to visualise and analyse your hledger data in a tabular GUI.
Ultorg goes farther than spreadsheets, with more powerful joins and layout. https://www.ultorg.com/resources > 1st video demonstrates its key features, and the 3rd video is a fascinating demo of analysing Quickbooks data.
Exporting to Ultorg via CSV
This method is perhaps the easiest.
-
Export a hledger journal as CSV, eg:
$ hledger -f examples/bcexample.hledger print -o examples/bcexample.csvor
$ hledger print -o main.csv -
Run Ultorg.
-
Click Add Data Source in the left panel, click Connect to File, and select the CSV file. Press enter to confirm the import settings.
-
The CSV file appears in the left panel, containing one table. Double-click on the table to open it in the main pane.
-
Group all the per-transaction columns: CMD/CTRL-click the headings of txnidx, date, status, and description, right-click the headings and choose Custom Group (CMD-I).
-
Now hide uninteresting columns, eg perhaps Row Number, status: CMD/CTRL-click the headings, right-click -> Hide (CMD-SHIFT-K). (Don’t hide txnidx, it seems to help date sorting.)
-
And sort by date: right-click date heading, Sort Ascending.
Exporting to Ultorg via SQlite
The results are much the same as the above, though you will also have an SQLite database to play with (see hledger and SQLite). This method requires Ultorg >=1.26. Note also ultorg#53.
-
Export a hledger journal as an Sqlite database. Eg:
$ hledger -f examples/bcexample.hledger print -O sql | sqlite3 bcexample.dbor
$ hledger print -O sql | sqlite3 main.db -
Run Ultorg.
-
Click Add Data Source in the left panel, click Connect to File, and select the .db file.
-
The db file appears in the left panel, containing one table. Double-click on the table (Postings) to open it in the main pane.
-
Group all the per-transaction columns: CMD/CTRL-click the headings of Txnidx, Date 1, Date 2, Status, Code, Description and Comment, right-click the headings and choose Custom Group (CMD-I).
-
Now hide uninteresting columns, eg perhaps Date2, Status, Code, ID, Posting Status: CMD/CTRL-click their headings, right-click -> Hide (CMD-SHIFT-K). (Don’t hide Txnidx, it seems to help date sorting.)
-
And sort by date: right-click Date 1 heading, Sort Ascending.
Things to try
-
Click the Amount column, then click the toolbar button that (by default) looks like a bar chart.
-
Click the toolbar button that (by default) looks like a table and select a different Auto-Layout Type, like Form (Multi-Column).
hledger and You Need A Budget
Related:
- https://www.artojonsson.com/blog/resetting-the-defaults-finances/
- https://github.com/vermiceli/ynab-to-ledger
- https://www.pgrs.net/2016/10/19/exporting-ynab-to-ledger/
- https://www.reddit.com/r/plaintextaccounting/comments/kwzjr2/ynab_to_hledgercontinuously/
- https://www.reddit.com/r/plaintextaccounting/comments/gyfplz/ledger_automated_transactions_matching_without/
hledger-web tips
Edit, upload, download
hledger-web does allow you to edit, upload or download the whole journal file, but for some time it has been undocumented. Use the –capabilities flag to add “manage” permission:
$ hledger-web --capabilities=view,add,manage
Then you’ll see a new spanner button to the right of the search bar. Click this to edit, upload or download your main data file or included files.
The url for the editform looks like this:
http://127.0.0.1:5000/edit/%2Fabsolute%2Fpath%2Fno%2Fsymlinks%2Fto%2Fdesired.file
See also: http://hledger.org/hledger-web.html#permissions
How to figure out journal entries
When you’re facing an unfamiliar bookkeeping situation where the journal entries aren’t clear, try this:
-
Write down the real world events to be modelled, in detail
-
Write down the things you want to know
-
Start with a simpler version of the scenario if necessary. If you get stuck below, return here and simplify more.
-
Discover which accounts and transactions are needed to model the situation in the most principled and correct way. Take one event at a time, and try to write the journal entry for it, using hledger to check that each transaction is balanced and the account balances make sense. If needed, return to step 3. Or consult books or the internet for ideas - but carefully, since their terminology and examples will often be different and confusing.
-
Experiment and discover reporting commands that show the things you want to know. If it seems not possible, you may need to revisit steps 3 or 4.
-
Now consider, are there simpler accounts/transactions/reports that might be more convenient, yet still good enough ?
-
Choose one approach. Document it if necessary so you’ll remember. Save or automate the reporting commands so you’ll remember those.
-
Put it into practice and refine over time to improve clarity and usability.
How to record journal entries
There are many ways to create hledger journal entries. Here are some options:
Interactively
With a text UI
- hledger add (or
ain hledger-ui) - hledger-iadd (or
Ain hledger-ui)
With a web UI
- hledger-web add form or edit form
- Paisa edit form
On a phone or tablet
- With one of the mobile apps
In a text editor
From scratch
- Write the entry in full, using journal format.
- As above, but also use editor addons to assist (like Emacs ledger-mode’s TAB account completion and M-q alignment).
From templates
- In Emacs with yasnippet configured (eg), enter the name of a snippet and complete with TAB
From past entries
- Find, copy and modify a past entry in the journal
- Or in Emacs ledger-mode, enter a date and partial description and complete with C-c TAB
From future entries
- Move the entry from a separate file where you prepared it ahead of time (eg future.journal)
At the command line
From scratch
- Write the entry using journal format, eg
or$ cat >>$LEDGER_FILE 2023-01-01 expenses:food $10 assets:cash <CTRL-D>printf >>$LEDGER_FILE '2023-01-01\n expenses:food $10\n assets:cash\n\n'
From past entries
- Install Ledger and use ledger xact DESC
From periodic transaction / auto posting rules
- Convert a periodic transaction rule (eg in future.journal) to an entry with
--forecast[=DATEORPERIOD] - As above, plus generate extra postings from auto posting rules with
--auto
From custom scripts
- If you have custom scripts to help with tasks, make them print or append journal entries when appropriate. Eg have your invoicing script add an invoice transaction to the journal.
By converting data
From bank CSV (or other character-separated values)
- hledger import plus CSV conversion rules (and a pre-cleaning script if needed)
- Or keep the CSV files forever and read them with
-f - Or regenerate journals from them with Full-fledged hledger or hledger-flow
From timeclock, timedot or other journal files
- Just read them directly with
-f - Or, hledger import them to main journal
From other apps or data formats
- Look for conversion tools and tips at https://hledger.org/cookbook.html#other-software
- Or at https://plaintextaccounting.org/#data-importconversion
- Or write a conversion script that prints hledger journal entries (or other supported format)
- As above, but save the output to a file and
importfrom that to get only new entries
Invoicing
Freelancers and businesses send invoices to clients to request payment.
See common journal entries and https://plaintextaccounting.org/Invoicing
Reports
With invoices and payments recorded as above, you can track unpaid invoices:
$ hledger bal receivable:supercompany
or list all invoices and payments:
$ hledger areg receivable:supercompany
or just invoices:
$ hledger areg receivable:supercompany amt:'>0'
or just payments:
$ hledger areg receivable:supercompany amt:'<0'
Creating Invoices
How to translate the data from your ledger into a professional-looking invoice you can send to clients ?
You can create the invoice manually or semi-manually, eg using a tool like Freshbooks, and copy-paste the numbers in.
Or you can automate this somehow. There are few ready-made tools for this, because needs are so diverse.
But you’ll find some useful starter scripts in hledger’s examples/invoicing directory.
See also
- examples/invoicing/
- https://github.com/kairosdotapp/kairos
- https://plaintextaccounting.org/Invoicing
Mobile apps
Entering expenses on the spot using a mobile device can be convenient. One of the challenges is finding apps that focus on making this efficient.
https://plaintextaccounting.org/#ui-mobile is the up to date list of PTA-specific mobile apps. Most of these are focussed on data entry, then export to a computer for reporting.
hledger-compatible apps
NanoLedger (Android, 2023-)
https://github.com/chvp/NanoLedger
https://f-droid.org/packages/be.chvp.nanoledger
Exports a h/ledger journal.
Cashier, Cashier II (progressive web apps, 2022-2024)
Cashier:
https://github.com/alensiljak/cashier
demo
Cashier II:
https://github.com/alensiljak/cashier-blazor
demo
These can be used on a computer or a phone, including offline. They can optionally import data from h/ledger using a server. They export a new h/ledger journal. They also provide some reports.
MoLe (Android, 2018-2024)
https://mole.ktnx.net
https://git.ktnx.net/?p=mobile-ledger.git
Connects to a hledger-web server, which you must have running somewhere your phone can connect to (eg a VPS or a VPN). Ie it can potentially add transactions directly into your main accounting system, without needing an export/import step.
Cone (Android, 2019-2021)
https://github.com/bradyt/cone
https://f-droid.org/packages/info.tangential.cone
blog
Exports a h/ledger journal. According to NanoLedger’s blog post, it had some bugs when the journal file is synced with Nextcloud, and lags if the file is pretty big. There was a report of data corruption.
Cone vs NanoLedger:
| Cone | NanoLedger | |
|---|---|---|
| Journal view screen | Transactions can be sorted by new to old or reversed, and elements are colored | Transactions can only be sorted by new to old, and elements aren’t colored |
| Transaction entry screen | The description field is free form | The description field is split into status field, payee field and note field |
| Modify old transaction | No | Yes. You can edit or delete old transaction, or use it as the template for a new one |
Apps with CSV export
Many other apps can export CSV, which hledger can read.
The usual workflow is that every so often you manually initiate a CSV export from the app. Often the app starts up a temporary HTTP server, and you can fetch the data to your computer with curl. Then you can use hledger’s usual CSV-reading or importing features.
GnuCash (Android)
https://github.com/codinguser/gnucash-android
google play
Mobile UI for the mature GnuCash accounting software.
XpenseTracker, BizXpenseTracker (IOS)
http://www.silverwaresoftware.com/XpenseTracker.html
Comprehensive and serviceable money & time tracking apps.
Eternity (IOS)
http://www.komorian.com/eternity.html
An excellent time tracking app.
Apps with other ways to export
Money Manager Ex (Android)
http://android.moneymanagerex.org,
https://github.com/moneymanagerex/android-money-manager-ex
Android port of the Money Manager Ex cross platform finance application.
The MoneyManagerExLib python library can be used to convert its db to *ledger format.
MyExpenses (Android)
http://www.myexpenses.mobi, https://github.com/mtotschnig/MyExpenses
Personal finance manager for Android.
Export to *ledger format: https://github.com/ony/ledger-myexpenses (2017)
Sync to/from Ledger: https://gitlab.com/IanTwenty/ledgermyexpenses (2023)
Multicurrency tutorial
Currency handling in PTA tools is a big topic. I wrote this tutorial in 2018. It is focussed on simple journal entries with implicit currency conversion. It places it goes pretty fast and does not explain itself well. But it contains a lot of small examples, and if you can follow each step, you will understand quite a bit about hledger’s behaviour. If you get stuck, let me know. See Cookbook > Multiple currencies for more.
Anya begins using hledger without any currency symbols. She adds some journal entries like this (not bothering with descriptions, either):
2018/11/01
income:gifts
assets:bank 1000
2018/11/02
assets:bank
expenses:food 500
She knows hledger is filling in the missing amounts, which can be seen with print’s -x/–explicit flag:
$ hledger print -x
2018/11/01
income:gifts -1000
assets:bank 1000
2018/11/02
assets:bank -500
expenses:food 500
The balance command with no arguments shows all balance changes. The total is zero, as Anya expects - each transaction sums to zero, and all transactions are included in this report, so the report also sums to zero:
$ hledger bal
500 assets:bank
500 expenses:food
-1000 income:gifts
--------------------
0
Unlike partial balance reports (omitting some accounts), which typically do not have a zero total:
$ hledger bal food
500 expenses:food
--------------------
500
Anya maintains a popular free software project. She remembers that she added a Liberapay button to the project website yesterday, allowing donations. Her native currency is rubles, but Liberapay pays out US dollars or euros.
She realises she had better start tracking currencies in her journal or things will get confusing. So she adds currency symbols throughout her journal:
2018/11/01
income:gifts
assets:bank ₽1000
2018/11/02
assets:bank
expenses:food ₽500
Thinking ahead, she sees that entering euro symbols will be a bit unergonomic on her keyboard. She thinks perhaps she’ll use standard alphabetic currency codes instead, and on the right-hand side:
2018/11/01
income:gifts
assets:bank 1000 RUB
2018/11/02
assets:bank
expenses:food 500 RUB
But she finds this a bit verbose. She decides to use single letters - R for rubles:
2018/11/01
income:gifts
assets:bank 1000 R
2018/11/02
assets:bank
expenses:food 500 R
Now her reports show the currency symbol:
$ hledger bal
500 R assets:bank
500 R expenses:food
-1000 R income:gifts
--------------------
0
And she is ready for multicurrency accounting. Just in time, because next day a donation of 10 euros arrives! She records it, using E for euros:
2018/11/01
income:gifts
assets:bank 1000 R
2018/11/02
assets:bank
expenses:food 500 R
2018/11/03
income:foss
assets:liberapay 10 E
Now she has a multicurrency journal, and the balance report shows both currencies:
$ hledger bal
10 E
500 R assets
500 R bank
10 E liberapay
500 R expenses:food
-10 E
-1000 R income
-10 E foss
-1000 R gifts
--------------------
0
However, it’s a bit confusing. The assets and income parent
accounts now have multicurrency balances, and each currency is
displayed on its own line. She tries
flat mode, and finds it clearer:
$ hledger bal --flat
500 R assets:bank
10 E assets:liberapay
500 R expenses:food
-10 E income:foss
-1000 R income:gifts
--------------------
0
But she has heard that hledger’s tabular output is best for multicurrency reports, always showing amounts on one line. She starts using that, adding one of the report interval flags (-Y/–yearly) to activate it:
$ hledger bal -Y
Balance changes in 2018:
|| 2018
==================++=========
assets:bank || 500 R
assets:liberapay || 10 E
expenses:food || 500 R
income:foss || -10 E
income:gifts || -1000 R
------------------++---------
|| 0
Anya requests a withdrawal of the Liberapay funds to her bank. Her bank holds rubles, so the euros will get converted. She’s not sure of the exact exchange rate or fees, but next day, when the transaction clears, she can see that 10 euros left her liberapay account and 750 rubles arrived in her bank account. She decides to just record that:
2018/11/01
income:gifts
assets:bank 1000 R
2018/11/02
assets:bank
expenses:food 500 R
2018/11/03
income:foss
assets:liberapay 10 E
2018/11/04
assets:liberapay -10 E
assets:bank 750 R
This is her first multicurrency transaction. She hasn’t written the exchange rate explicitly, but the manual says hledger can figure it out. It seems to work:
$ hledger bal -Y
Balance changes in 2018:
|| 2018
===============++==============
assets:bank || 1250 R
expenses:food || 500 R
income:foss || -10 E
income:gifts || -1000 R
---------------++--------------
|| -10 E, 750 R
However, two things surprise her. First, where has the liberapay account gone ? She remembers that balance reports hide zero-balance accounts by default, and adds -E/–empty to show it. (She also notes that zero amounts are displayed without a currency symbol, and would be a little clearer with currency symbols on the left):
$ hledger bal -YE
Balance changes in 2018:
|| 2018
==================++==============
assets:bank || 1250 R
assets:liberapay || 0
expenses:food || 500 R
income:foss || -10 E
income:gifts || -1000 R
------------------++--------------
|| -10 E, 750 R
Second, the balance report is now showing a non-zero total. The individual euro and ruble totals look correct, but why isn’t it zero ? Is the journal unbalanced ?
Anya asks for help on the #hledger IRC channel and is advised to add the -B/–cost flag. Sure enough, the total is now zero:
$ hledger bal -YEB
Balance changes in 2018:
|| 2018
==================++==============
assets:bank || 1250 R
assets:liberapay || 10 E, -750 R
expenses:food || 500 R
income:foss || -10 E
income:gifts || -1000 R
------------------++--------------
|| 0
But now the liberapay account, which should be empty, is showing a positive euro and negative ruble balance. As if one had not been converted into the other. Why is this ?
With a little help, Anya goes troubleshooting. Inspecting the multicurrency transaction with print -x (and a date filter to exclude the rest) shows how hledger has parsed it:
$ hledger print -x date:20181104
2018/11/04
assets:liberapay -10 E @@ 750 R
assets:bank 750 R
The manual makes this a bit clearer. Anya wrote the entry in costs style 3 (“let hledger infer the price that balances the transaction”). hledger has converted this to style 2 (“@@ TOTALPRICE after the amount”), recording that the 10 euro were priced at 750 rubles in this transaction.
With -B added, the 10 euro is converted to its cost in rubles:
$ hledger print -x date:20181104 -B
2018/11/04
assets:liberapay -750 R
assets:bank 750 R
The register command shows how the balance reports above calculate the liberapay balance. Without -B: 10 euro are added, 10 euro are removed, the liberapay account’s end balance is zero:
$ hledger reg liberapay
2018/11/03 assets:liberapay 10 E 10 E
2018/11/04 assets:liberapay -10 E 0
With -B: 10 euro are added, 750 rubles are removed, the liberapay account’s end balance is “10 euro, -750 rubles”. (With each currency on its own line, again. Also, it seems that register aligns the account name with the top amount, unlike the balance command):
$ hledger reg liberapay -B
2018/11/03 assets:liberapay 10 E 10 E
2018/11/04 assets:liberapay -750 R 10 E
-750 R
In summary, it seems that the balance report must sum either the primary posting amounts (bal), or the cost amounts (bal -B), consistently for both the account balances above the line, and the total below the line. Otherwise the total would be incorrect. Which means that one or the other of these will be displayed as an unconverted multicurrency amount.
Anya decides to find out more about the other currency-related flag: -V.
TBD:
-
declaring a market price corresponding to the price in the fourth transaction ( P 2018/11/01 E 75 R ) and adding -V will show everything completely in rubles (with or without -B, at least in this case), preserving the zero total
-
declaring an accurate market price instead ( P 2018/11/01 E 74.91 R ), there will be a small non zero total, which corresponds to the gain/loss due to exchanging at a slightly different price. After adding an explicit gain/loss transaction, the zero total is restored.
News
Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat,
every Friday morning or any time, tagged with TWIH:.
See also: Release notes, Recent discussion.
This Week In Hledger 2024-08-09
sm Offline for a while, not much to show in last three weeks. Improved bad regexp error message; bin/ updates; timeclock, cookbook, mobile doc updates. Writing a new course or book on time tracking.
Quotes
it is very satisfying to get a report of my last 10 years swiftly over the weekend. Thanks for the software :) – droidoneone
This Week In Hledger 2024-07-19
sm
- bs,cf,is: show interval in report title
- check recentassertions: improve message
- cli parsing, config file: fixes, new example config
- ui menu screen: consistent control/display of narrowed period
- web: guess a more robust base url when –base-url is not used
- web: require a http[s] scheme in –base-url
- docs: Date adjustments, FAQ: How to tidy a journal
- daily support
Misc Michael Rees posted a PR implementing flexible register sorting (#2211). Adolf Szabo released placc, a free PTA app built with nim, not yet open source.
Quotes I just moved my whole financial life over to hledger and oh my goodness I’m so much happier (I dumped both QuickBooks and Quicken, which I’d been using previously).
This Week In Hledger 2024-07-12
sm Command line parsing testing & fixes; hledger-ui menu screen keys; example shell scripts updates; doc updates (add, config files, hledger-ui).
Quotes
-
It’s hugely valuable to have all of your financial data in one, human-readable place. Not only to you, but to whomever might be executing your will ;)
-
I have it running on my laptop at all times (a window in my tmux config displays various reports on my accounts like current balance, uncleared balance, uncleared transactions, etc.). I find it helps me to be mindful of my money if it’s only a couple keypresses away.
This Week In Hledger 2024-07-05
sm Testing/refinement of CLI, config file support; Tiller csv rules example; explain shebang commands better in hledger-script-example.
Quotes
+1 for plain text accounting. I’m using hledger to track my finances. I do use the web UI to submit information but I edit the plain text store extensively.
Now I use hledger, which has been fantastic. I back up the file to a shared drive that’s sync’ed to my phone, so every time I get a message from the bank or pay with cash, I can immediately note it down (this would have been much less workable before I found the “cone” app) and track my spending across dozens of accounts.
This Week In Hledger 2024-06-28
sm Worked on CLI and config file support.
Quotes
-
Every time I think hledger is missing something I always find either the exact feature I was looking for or two things that coupled together accomplish what I need it to do. –vm
-
It just keeps getting better –glguy
This Week In Hledger 2024-06-21
sm Updated the hledger-register-max script, fixed a problem with just ghci* scripts, refactored the main procedure and command line processing, worked on config file support.
Misc recent discussions: https://hledger.org/support.html
Quotes
-
I liked that hledger had a few reports that ledger didn’t have, like real balance sheets and income statements. Both integrate well with emacs, but their journal files differ in slightly incompatible ways, so they’re not drop-in replacements. Both are very fast.
-
My primary consumption method is hledger-ui, a nice curses-based UI. It’s got a
watchmode so will update in real-time as you make edits to the source database, which is nice.
https://hledger.org/news.html#this-week-in-hledger-2024-06-21
This Week In Hledger 2024-06-14
sm
I fixed an old bug with account type declarations in multiple files, and an incompatibility of –tldr with tealdeer. I automated the updating of option docs in manuals, and worked on code cleanups, refactoring, and removing tech debt. And I worked on various improvements:
- tabular balance reports now show an outer table border and inter-column borders with –pretty, and they have better layout and headings in text and html output.
- better/clearer command line processing
- a config file system for next release
Doc updates: box-drawing characters, –pretty, –tldr.
Misc The docker image for hledger 1.34 is out. And for recent discussions see: https://hledger.org/support.html
Quotes
-
I switched away from Quickbooks, years ago, to plain text accounting. Couldn’t be happier. I can do everything better using hledger/ledger, git, vim and make.
-
I’ve switched all my accounting over to hledger (a Haskell-based and slightly more feature-filled version of the original) and I’m SUPER happy with it so far.
https://hledger.org/news.html#this-week-in-hledger-2024-06-14
This Week In Hledger 2024-06-07
sm Released hledger 1.34, fixed some CI issues. Improved docs:
- cli, ui, web: synopsis, options cleanup/consistency
- command flags are now included in manuals (and Shake cmddocs updates them)
- diff, prices: improve help layout
- environment: correction, NO_COLOR does not override –color=yes
- Generating data: rewrite
- ghrelnotes: fix mac, linux install commands
- ghrelnotes: link to the Build from source doc
- secondary dates: officially deprecate secondary dates and –date2
- web JSON API, cli JSON output: link to OpenAPI spec
Misc recent discussions: https://hledger.org/support.html
Quotes
-
Ledger and Hledger are some of my favourite tools.
-
Thank you for creating such an awesome tool. I absolutely love hledger. I’ve been using it for more than a year, and I’ve become more responsible with my finances as a result. Before I started using hledger, I knew nothing about accounting. In fact, I was oblivious of my personal finances. Now, I’ve not only started keeping track of my finances, but I’ve also started investing and keeping a budget. –aaditmshah
https://hledger.org/news.html#this-week-in-hledger-2024-06-07
This Week In Hledger 2024-05-31
sm
- integrate tldr pages as –tldr
- make help -m, –man jump to TOPIC properly
- –color now also accepts y or n, like –pretty
- CLI commands list and options cleanups
- make checkembeddedfiles more robust
- tools: just twih date fixes
- release prep
Doc updates:
- Unicode characters: mention UTF-8 on windows
- update/simplify general options list
- sync command descriptions with tldr docs
- tldr contributing docs
- website: properly redirect old /FAQ path; tagline tweak
- PTA website: add first sponsor logo; mention
hledger-sankey
Misc: for recent discussions, see https://hledger.org/support.html
Quotes
-
I’ve switched all my accounting over to hledger (a Haskell-based and slightly more feature-filled version of the original) and I’m SUPER happy with it so far.
-
A little daily-ish data-entry-and-reconciling ritual, with a pleasant tool setup, can be quite satisfying! I actually look forward to it. (That right there is part of my PTA success story. I used to suffer a lot of stress around all things finance.)
This Week In Hledger 2024-05-24
sm Actually this fortnight, since I missed a post. Last week I finished getting hledger back into stackage nightly, fixed build errors with GHC 8.10, and updated github workflows. This week I worked on release automation, flattened and simplified page tables-of-contents on hledger.org, updated/added more hledger docs in the tldr project, and fixed breakage in the hledger-install.sh script on non-windows platforms.
Misc
- hledger-Excel exports from excel to journal format.
- For recent discussions, see https://hledger.org/support.html.
Quotes
After coming from hledger, I did try firefly, but, stuck with hledger due to how amazingly powerful it is with nothing but a text input.
Switched to beancount … I really didn’t lose any of my reporting flows that I had with (h)ledger… I just made scripts that convert the beancount journal on the fly, run whatever I was used to with hledger, and then delete the temporary file.
This Week In Hledger 2024-05-10
sm
- imp:
-Ican now be overridden by-sorcheck - imp: commands list updates
- doc updates: commands list, report start & end date, smart dates, balance command
- site updates: install, FAQ reorg
Misc
- Docker image for 1.33.1 is available
- For recent discussions, see https://hledger.org/support
Quotes
I use hledger for my solo-company. It is really great, and very easy to understand what is going on. Highly recommend. –koeng
Just start with hledger-ui and like the article said: mess it up a bunch and keep fixing it or starting over. Pick one particular “zone” of finances (eg: monthly bills) and track only that part of it for a while. You’ll figure it out! –ramses0
This Week In Hledger 2024-05-03
sm
- fix: show trailing decimal mark on cost amounts too, when needed
- imp: Revert problematic process-1.6.19.0 bound, it seems not strictly needed #2149
- Released hledger 1.33.1
- dev script updates
- fix: journal: parse include directives with trailing whitespace
- imp: support ghc-debug for analysing memory/profile info
- imp: cli: commands list reorg, cleanups
- imp: check: check ordereddates before balance assertions, when both are enabled
- imp: web: provide a basic openapi specification of the current HTTP-JSON API
- doc updates: github release windows install instructions, check command, Contributor Quick Start, Developer FAQ, misc site tweaks
- examples: custom paypal csv rules
Misc
- https://kairosdotapp.github.io/kairos invoice generator, timedot compatible
You can see recent discussions via the links at https://hledger.org/support.html.
Quotes
It’s been a while since I’ve been in this room, but I’ve been using hledger all the while. It continues to be incredible. –daveakre
This Week In Hledger 2024-04-26
sm
- imp: run checks in a well-defined order, and tweak that order
- imp!: check ordereddates: drop –date2 support; check primary dates only
- fix: balcmds: show digit groups marks in html output again [#2196]
- imp: web: support base64 >=1.0
- tools: retire the main Makefile
- tools: relnotes.hs: generate/update release notes from changelogs
- tools: md-issue-refs.el: manage issue links in markdown
- examples: vanguard, fidelity csv rules updates
- doc updates: import, csv amount decimal places, charts, dev docs, install page.
You can see recent discussions via the links at https://hledger.org/support.html.
Quotes
Trust me, if you need to run some complicated analysis, (h)ledger has your back and you can find how to do it in the docs. –lvass
Thank you for this remarkably beautiful and useful tool. –yse
This Week In Hledger 2024-04-19
sm Worked on release process, github workflows, platform build issues, mac arm release binaries, CLI commands list, a hledger add ANSI bug, misc doc updates, and released hledger 1.33.
Misc
You can see recent discussions at: https://matrix.hledger.org https://list.hledger.org https://forum.plaintextaccounting.org/latest https://www.reddit.com/r/plaintextaccounting/new https://fosstodon.org/search #hledger
Quotes
Been using ledger and hledger for about four years, love them dearly. I find ledger + Emacs ledger-mode the most convenient for daily use. Hledger has a somewhat nicer way of formatting reports on the CLI, making it useful for creating ad-hoc reports and digging into your data. –compns8-ng
I’ve been using hledger … [it] has more features like balance sheet, income statement generation with a plain text file for the last 3 years and it’s been working out great. –rwbt
This Week In Hledger 2024-04-12
sm Investigated/refactored/discussed a problem with boolean query expressions; disallowed date: in OR expressions #2178. Required process >=1.6.19.0 for HSEC-2024-0003. Updated docs: balance assertions, balancesheetequity.
plaintextaccounting.org updates: FAQ, Borrowing and lending, Investing and trading, Shared expenses, project ledger.
Misc
You can see recent discussions at: https://matrix.hledger.org, https://list.hledger.org, https://forum.plaintextaccounting.org/latest, https://www.reddit.com/r/plaintextaccounting/new, https://fosstodon.org/search -> #hledger. Feel free to share interesting links (they don’t have to be new) at the PTA forum.
Quotes
There are few things better than doing my taxes every year and being able to confirm every number on every form with simple #hledger queries (which lets me double check both my records and the forms themselves). –Michael Rees
I combine hledger csv output with Jupyter to generate the specific reports and charts I need. I.e business specific versus combined for taxes. –abhiyerra
This Week In Hledger 2024-04-05
sm Doc updates (#2189, #2191, Cookbook#investing, A tax reporting setup). Added Brian Victor’s hledger-txnsbycat.hs to the Scripts page.
On plaintextaccounting.org I started a project finance/donations page and public ledger; moved blog links off the front page in favour of the PTA forum; added a new video in Tamil language; updated Investing and trading, FAQ.
Misc
Dmitry Astapov fixed the roi command’s handling of –value (#2190).
Ellane W raised thoughtful questions about when to use PTA vs simpler tools like paper.
lil5 announced a Go language client API for hledger-web.
You can see the recent discussion at eg https://matrix.hledger.org, https://list.hledger.org, https://forum.plaintextaccounting.org/latest, https://www.reddit.com/r/plaintextaccounting/new, https://fosstodon.org/search -> #hledger. Feel free to share interesting links (they don’t have to be new) at the PTA forum.
Quotes
Having hledger have all my accounts, personal and business and being able to filter out appropriate reports has been great for seeing things like is the business a money pit or am I actually growing wealth over time. –abhiyerra
I use hledger and I love it. It has a learning curve (especially if you’re new to double entry accounting) but it’s rewarding.
This Week In Hledger 2024-03-29
sm Doc rewrites, error message improvements.
Misc
- More discussion of hledger-interest, a call for improvements, and a pull request by Dmitry Astapov.
- Some discussion of exporting to Excel/spreadsheets.
- How much attention to bookkeeping is just right ? blog post
Feel free to share your experience reports or interesting links (they don’t have to be new) on the PTA forum.
Quotes
- The initial setup of the books is pretty time consuming especially if you have a lot of accounts. I recommend starting your most important accounts and adding as you go along. –abhiyerra
This Week In Hledger 2024-03-22
sm
I tried getting hledger-sankey (generates Sankey diagrams of cash flows) working on my real world ledger, and contributed a patch.
Misc
Ilya Konovalov made multi-section balance reports always show a section total even when empty, fixing their CSV output. [#2186]
You can see recent discussions at:
https://matrix.hledger.org https://list.hledger.org https://forum.plaintextaccounting.org/latest https://www.reddit.com/r/plaintextaccounting/new https://fosstodon.org/search #hledger
Feel free to share interesting blog posts (they don’t have to be new) at the PTA forum.
Quotes
A key advantage of hledger, and why I migrated from ledger, is that hledger internally orders transactions chronologically, whereas ledger inexplicably orders them in the order they appear in the file, irrespective of the date attached to them.–chromatin
hledger can validate date order, that seems to eliminate copy-paste-fail-to-update-date for me. –yencabulator
The reason I have hledger installed is for check ordereddates. –lvass
This Week In Hledger 2024-03-15
sm No hledger dev work this week. The new PTA forum continues to work well.
Misc docker-finance, a new cryptocurrency-supporting workflow system using hledger-flow, and the older workflows Full-fledged hledger, hledger-flow and rtrledger, have been added at https://plaintextaccounting.org/#workflows.
To save time, I won’t summarise discussions this week; you can see them at:
https://matrix.hledger.org https://list.hledger.org https://forum.plaintextaccounting.org/latest https://www.reddit.com/r/plaintextaccounting/new https://fosstodon.org/search #hledger
Feel free to share interesting blog posts (they don’t have to be new) at the PTA forum.
Quotes
-
Way less painful than QuickBooks if you’re also a programmer or adjacent. –dmoy
-
My favorite part of the (extensive) Haskell API is
Hledger.Cli.Script. It re-exports all the most commonly used functions and data types, meaning you’re just one import away from everything you need to get started.–wbadart
This Week In Hledger 2024-03-08
sm I fixed some doc issues on hledger.org: code blocks can now be scrolled normally, and are cheerily color-coded instead of drab grey. The 1.33 release is still pending. Another gnarly release blocker remains to fix, and I was burned out on those. Instead, got deeply involved in setting up a new site for PTA discussions, https://forum.plaintextaccounting.org.
Robert Nielsen On the hledger fan website, I look at a planning mistake that’s easy to make in hledger or any planning. See it at https://hledgerfan.com/dont-make-this-planning-mistake-with-hledger.
Misc Jonathan Dowland fixed match group references in csv rules [#2158].
Matrix chat Haskell software for Canadian tax prep, aregister print order, reporting prices effective on a date, filtering by balance size, migration issues, mint.com closure, discourse for PTA discussions, combining CSV records, CSV conversion issues, CSV rule if tables, bookkeeping success stories, downloading prices for a date range..
- ANN: forum.plaintextaccounting.org
- CSV rules: if tables, do you use them ? want them ?
- [hledger] Auto posting that excludes previously matched postings
- Question: Hledger csv first account names in csv files?
- Discourse for Plain Text Accounting discussions ?
- online content platforms lock-in
- PTA forum
Quotes
-
Journal’s greenyellow really makes it pop and I think it will make us rich! –sm
-
I started setting up hledger this year and I’m with you for a small business it is absolutely great. –abhiyerra
-
In summary for me: hledger has better csv importing system and ledger has support for lots. I use both. –faustlast
This Week In Hledger 2024-03-01
sm A lot of work this week: bugfixing, updating for GHC 9.8, and improving docs. We weren’t ready to release 1.33 on March 1st, but it’s near.
- web: show zero amounts with their commodity symbol [#2140]
- fix expr:’s OR-ing of open-ended dates [#2177]
- made stats output more private by default, added memory stats
- in unbalanced transaction error messages, show small imbalances more precisely
- fixed all ghc 9.8 partial head/tail warnings
- resolved remaining ghc 9.8-related issues in our dependencies
- updated all stack configs, switched to nightly/ghc 9.8 for dev builds
- added support for other unicode space characters in numbers
- added –round option to close, like print’s
- fixed potential display of ambiguous balance assertion/assignment amounts [#2176]
- refactored the budget report and fixed wrong omitting of parent accounts [#2071]
- made budget report’s CSV/TSV output show zero instead of empty cells
- worked on a bugs dashboard
- doc: fix description of commodity directive’s scope [#2135]
- doc: explain hledger’s transaction balancing
- doc: discuss debits, credits, and sign
- doc: hledger manual cleanups, fixes, flow improvement
- doc: FAQ: update why was hledger created
- doc: hledger and ledger: update performance notes
- plaintextaccounting.org: link https://lemmy.world/c/plaintextaccounting, a reddit alternative
Robert Nielsen On the hledger fan website, I take a look at needs, strategies, and spending. Also taxes and revisiting lowercasing. https://hledgerfan.com/needs-strategies-and-spending
Misc
Matrix chat wikipedia page, CSV rules, “Personal accounting in Ledger” book, unicode spaces in numbers, rvgp, loan bookkeeping, Open Collective, hledger manual size, file naming, emacs, visual studio code, aggregate shares bookkeeping..
- Forex Accounts Receivable, timing difference between invoice and payment
- Unexpected behavior using ‘expr:’
- Examples on hledger’s web page manual are cut off
- rvgp: an open-source PTA tool, that brings a proper build chain, to your accounting. Automates your Reconciliation, Validation and financial Graphing, in ruby.
- [Beancount] CLI tool to import Wise multi-currency transactions
- How to install Ledger CLI for Windows?
- [beancount] Stock Purchase Plan Qualifying Disposition
- Design patterns or idioms?
- hledger register query only on Tuesdays
Mastodon hledger manual size, switching from gnucash, more on the reddit IPO..
Quotes
Quite happy with my switch from GnuCash to hledger: already automated importing the (broken) CSV my bank provides, and getting an overview of average monthly expenses is a one-liner. –rigrig
The other nice part of hledger is the web UI (hledger-web), which is handy for checking out your transactions. –peterhajas
This Week In Hledger 2024-02-23
sm Miscellaneous updates:
- use a symbolic link’s target’s directory for relative include paths
- interpret glob patterns in $LEDGER_FILE
- implicitly declare the date/date2/type tags
- add close –assertion-type option
- tweak close –assert’s default description
- stack:9.8: can now build all packages
- doc: add Text encoding section
- doc: clarify description/payee/note
- doc: improve the tags doc
- doc: clarify combining csv matchers
- plaintextaccounting.org: Choosing accounts tips
Robert Nielsen Inspired by a recent podcast, I look at the theme of getting the most good from limited resources. Oh, and I don’t forget to mention hledger. 🙂 https://hledgerfan.com/creating-a-positive-impact
Misc
Jonathan Dowland added a dark theme to hledger-ui.
- Forex Accounts Receivable, timing difference between invoice and payment
- Balancing “equity:conversion” split across postings.
- How do you all handle grouping your transactions? (business)
- hledger: csv rule to match amount higher or equal to 200
- hledger periodic transaction every month except two
- Mass edit journal entries based on a filter
Mastodon Fund accounting, PTA sign convention, unspaced account names, reddit monetisation..
Matrix chat Tools for investment tracking, description/payee/note semantics, tag checking, arithmetic amount expressions, Money Manager EX, CSV conversion situations, categorising tools, utf-8 encoding, timedot budgets, tax preparation, if tables, payee cleanup..
Quotes
-
Thank you for writing hledger (and -ui and -web), it’s wonderful work. –Michael Iles
-
Use physical order to create mental order.
This Week In Hledger 2024-02-16
sm Not much time for hledger hacking this week; support and doc tweaks, and an updated example CSV rules file for Daedalus (Cardano wallet software).
Robert Nielsen On this week’s hledgerfan post, I show how the “include” statement solved a problem for running an hledger command over several data files: https://hledgerfan.com/include-was-the-key
Misc
Matrix chat Accounts vs tags, –serve-api and -X, balance assertion order, hledger.org tls cert, a visualisation of hledger compile times, stateful CSV conversion, chatGPT..
- Balancing “equity:conversion” split across postings.
- How to set the opening balance in a foreign currency account?
- Treasury Direct t-bills
- Can hledger import use account mappings from previous transactions?
- Is it possible to sort sub-accounts in hledger reports?
- Multicurrency cash journal
- Necessity of importing history
- Open Source Software Supply Chain
- A
:hledger:icon is now available on fosstodon.org thanks to @nobodyinperson and @mike.
Quotes
I just discovered hledger about a month ago after being fed up with YNAB’s costs and issues. I love hledger. It’s amazing. Thank you for all your hard work! –megagram
Work smoothly and steadily. Don’t rush.
This Week In Hledger 2024-02-08
sm
I worked on refactoring and debugging the budget report for https://github.com/simonmichael/hledger/issues/2071.
Also, tweaked close tags, docs, and added to the PTA FAQ: https://plaintextaccounting.org/FAQ#where-are-debits-and-credits-why-are-some-balances-negative.
I shared this time report that I like:
# what happened (this week by default ?) Show simplified time categories, largest first, grouping some similar subaccounts together.
@twhat *ARGS:
hledger -f "$TIMELOG" bal -b thisweek -e tomorrow -WATS -2 --alias '/.*:(res|adm|it)\b/=_:\1' "$@"
Robert Nielsen In this week’s post, I share my R program for generating random ledger/hledger transactions. Useful if you want to have some dummy transactions for showing how some feature of hledger works without having to use real data. https://hledgerfan.com/the-code-to-generate-random-hledger-transactions
Matrix chat CSV conversion, bookkeeping situations, mixing balances and changes in a report, include vs -f, tracking saving goals, amazon prime fees, paisa..
Mail list
Foreign currency accounting, adding custom tags with close, multiple postings vs multiple transactions..
- Need help with tracking a goal
- Exporting Paisa interactive visualizations
- [hledger] How do I get a specific tag column in register report
- Do you guys enter every transaction manually
- Thoughts on Beancount and PTA
- Is it possible to track mileage/mpg with ledger?
- Can I use ledger for time tracking and conversion from hours to USD?
Mastodon ..crickets..
Misc
Zegnat fixed line breaks in error messages displayed in hledger-web, making them much clearer: https://github.com/simonmichael/hledger/pull/2163
reesmichael1 made ssv: and tsv: work as file path prefixes, like csv::
https://github.com/simonmichael/hledger/pull/2165
jmtd made a dark theme for hledger-ui, testers welcome: https://github.com/simonmichael/hledger/pull/2167
brplot is a fast plotting tool for command line or wasm: https://news.ycombinator.com/item?id=39319191
Quotes
If you know how to get your transactions downloaded from your bank and don’t ever want to leave the command line, hledger is great for you – I’ve been using hledger since 2019 and love every minute of it.
This Week In Hledger 2024-02-02
sm
I cleaned up issue labels, updated the ISSUES page, refined the user pain score,
worked on an issues dashboard prototype,
tweaked the tags on close’s new –assert/–retain modes,
and shipped hledger 1.32.3.
Robert Nielsen Working program to generate fake hledger transactions
Matrix chat topics CSV rules, –pivot, queries, -f and glob patterns, QA and bug stats, close, 1.32.3 release, forecasting, rewriting account names, decimal correctness, VAT, mixing changes and balances in a report, tracking subscribers
Mail list 1.32.3 release, foreign currency accounting
- Technical Debt
- Is this possible with hledger: tracking ACB of RSUs in foreign currency and deemed dispositions when transferring to registered investment accounts?
- Correct way to track divorce / partner withdrawal in PTA
- Hledger amount comparison
Quotes
-
SM: too.. many.. bugs..
-
Aankhen: I’m not encountering hledger bugs. It’s just one of the most solid things I’ve ever used.
This Week In Hledger 2024-01-26
sm: Heavy work on
- 2151
closedesign/docs/features - 2150 a balance assertions regression
- 2153 a performance regression that has been with us since 1.29
And benchmarking generally. You can read some interesting new performance findings and help to verify or correct them at https://github.com/simonmichael/hledger/issues/2153.
Robert Nielsen Creating phony hledger transactions
Matrix chat topics: All the usual support topics (csv rules! reporting! journal entries!) and packaging - close - files layout - equity - Haskell learning resources - performance
Mail list: Breaking changes in hledger’s Haskell API and JSON API/output, coming in 1.33.
Reddit: Nothing hledgerish this week, but check out:
- Gainstrack - a more accessible plaintextaccounting for personal networth tracking.
Mastodon: Crickets.
Quotes
-
hledger has a decent front-end web UI, it runs as a local server on your machine by default. The filter and searching on this view is quite good, and visualization is decent.
-
With version control and deterministic reports, you can be late, slow, make mistakes, but still keep moving incrementally towards accuracy and clarity.
This Week In Hledger 2024-01-19
sm I updated issue tracker labels and ISSUES and REGRESSIONS pages. On plaintextaccounting.org, I added tables of contents on all pages, tightened bullet lists, ran a link checker and fixed broken links, consolidated to a single more prominent PTA FAQ, and wrote a new “How to organise files” answer.
Robert Nielsen After much procrastination, I finally do the actual changes I had planned for my hledger data. Sed was the tool of choice, and you can read about it at https://hledgerfan.com/first-step-in-closing-out-2023.
Matrix chat docs, csv rules, reports, nix, flycheck-hledger error regions, lot tracking, paycheck entries, annextimelog, tags, paypal, invoicing entries, matrix clients, balance assertions, pivoting, file organisation, receivables, account types
Mastodon 4-year regressions stats
- Tracking an asset like stocks and their growth
- Protecting financial data with PTA
- hledger-flow year specific rules file
- hledger: import from source with different decimal marks
- Any scripts/documented methods for importing nYNAB (web) data into PTA?
Misc hamess has annotated the new features in version 1.32’s manuals.
Quotes
Whoa this is amazing. And really easy!
I love hledger and have a few scripts to convert downloaded CSVs from various institutions into the appropriate format and dedupe any overlap if necessary. Once a month I download a bunch of statements, run a script and have all of the data available to me.
This Week In Hledger 2024-01-12
sm:
This week I worked mostly on support and thinking/writing about improvements to close and the new file/periodic files workflow.
I introduced some new terminology to distinguish starting/ending files vs opening/closing accounts, which are unrelated events.
I added new --assert and --assign modes to close and clarified a problem with using balance assignments for starting balances.
I reviewed status and proposed some changes at Better UX around starting and ending files ? #2151 -
please take a look and give your thoughts if you’re interested in this.
Over on https://plaintextaccounting.org I checked and fixed the remaining broken links; and surveyed and linked all the hledger and PTA videos visible on youtube (they’re fun, check them out).
nobodyinperson: TWIH: I published annextimelog v0.10.1 and it’s now in a state I would cautiously call useable for something practical as one can now record, delete and also edit logged time periods. It’s a cli git-annex based time tracker focusing on conflict-free syncing between devices and flexible tagging. It has timeclock output so can be piped into hledger, e.g.
atl ls -a project=hledger -O timeclock | hledger -f timeclock:- bal -D
https://pypi.org/project/annextimelog
Robert Nielsen: Getting ledger-mode to work again!
Matrix chat topics: CSV rules , Downloading, workflows , MCC codes , PTA videos , PTA site link checking , nix , just , cost basis , emacs & VS code , annextimelog , reporting , SM’s time and tasks dashboard
- how far in advance should I put anticipated expenses in liabilities:accountspayable ?
- Better UX around starting and ending files ?
- a single, cumulative journal, or annual journal files ?
- [hledger] Year Closing
- How to handle currency exchange via different accounts?
- Regexp in rules file
- Shared expenses bookkeeping
- just
- flycheck-hledger
- importing from your forecast
Open Source Collective, our fiscal host for project funds on Open Collective, may reduce transaction fees: between 15th January and 15th April, you’ll see a platform tip request on every new contribution on projects hosted by Open Source Collective. If the trial is a success Open Source Collective will lower our fee on contributions through opencollective.com, making donations more cost-effective for our Collectives.
Quotes
-
each time I get the TWIH ping I get reminded to use hledger again :D But it only shows me how bad I am at managing my money 🙈😬😅
-
hledger is so awesome. I just had to do my entire accounting for the last 1½yrs (because I’m a lazy slob) and thanks to hledger’s rule files, it took just one evening.
This Week In Hledger 2024-01-05
sm: I released hledger 1.32.2, which fixes bugs and adds hledger-ui on Windows.
In master I worked on
- fix check tags to ignore modified, _modified tags automatically #2148
- fix add,import,web: really avoid filenames ending with . on Windows #1056
- improve GHC 9.8 support
- add –assert and –assign modes to
close, eg to help try alternate file migration workflows.
In docs:
- RELEASING: process updates; changelogs are now updated only in release branch
- REGRESSIONS: split the bounty between finder and fixer
- sponsors: improve Open Collective tiers, fee info, avatar display
- add: clarify that
adddoes not add a default commodity symbol #815 - auto postings: clarify
- examples, examples/invoicing: expand READMEs, clarify status
Forgot to mention last week:
- Did some cleanup on https://plaintextaccounting.org, structure and links should be a little clearer.
- Spotted a new PTA app: https://github.com/zhang-accounting/zhang
Robert Nielsen: TWIH: Because I use Emacs, better Emacs = better hledger. See how Emacs guru Prot helped me: https://hledgerfan.com/better-emacs-better-hledger/
Misc
-
https://hub.darcs.net/thielema/nedit/browse/hledger.nedit A configuration for minimal syntax highlighting of HLedger journals in NEdit and XNEdit
-
https://blog.emacsen.net/profit-first-plain-text-accounting.html
-
https://blog.emacsen.net/profit-first-constraints-plain-text-accounting.html
-
https://www.reddit.com/r/plaintextaccounting/comments/18vvvbk/data_is_wider_than_the_terminal/
-
https://www.reddit.com/r/plaintextaccounting/comments/18x0t0m/how_to_map_goals_with_investments/
-
https://www.reddit.com/r/plaintextaccounting/comments/18zgwjo/hledgerui_for_windows_where/
-
https://www.reddit.com/r/plaintextaccounting/comments/18zxlbn/hledger_year_closing/
Quotes
-
Plain-text accounting is great. I choose hledger for the accounts of a small side business that doesn’t warrant hiring an accountant.
-
I love hledger and have a few scripts to convert downloaded CSVs from various institutions into the appropriate format and dedupe any overlap if necessary. Once a month I download a bunch of statements, run a script and have all of the data available to me.
This Week In Hledger 2023-12-29
sm: Fixed a tricky csv rule parsing regression #2134 and a debug logging bug.
Improved the project Justfile, improved docs. I also improved and “described” my time and task tracking dashboard; see https://hledger.org/time-and-task-dashboard.html. (Please accept Mr. Goldberg’s excellent diagram until I have time to make my own.)
And the usual support and online chatter.
Robert Nielsen: How -U helped me after making an error after mistakenly entering the wrong transactions: https://hledgerfan.com/fixing-my-mistake-after-accidentally-adding-pending-transactions
Misc: Lots of reddit posts this week, including some hledger-related ones:
- Is there any plain text accounting software which uses standard debits and credits?
- Is it possible to exclude certain accounts from a report?
- Finding an accountant
- Modelling
- Hledger rules file: Multiple postings for Revolut fees
- Help migrating my personal budget system
And also some lively discussion on the mail list, including this proposal to simplify file opening and closing balances.
Quotes: The main thing that made me switch to hledger was the ease of autogenerating transactions into a plain-text format. … GNUCash made recurring transactions easy but made it a lot harder to generate split transactions according to fixed splits that I regularly engage with my friends/family in. With hledger I have a system of taking CSVs, classifying transactions using a stack of Python scripts and using them to generate hledger importable files. I go by hand and annotate transactions that my scripts couldn’t classify/split, and then I import it into my main hledger file. I store all my inputs and intermediate imports in a git repository along with my main ledger file for auditability and to look at annotations. The ease of automation just can’t be beat with PTA but it’s definitely harder to use if you don’t have a preference for TUI work. Ease of writing importers was a huge driving factor when I switched to hledger from GNUCash.
This Week In Hledger 2023-12-22
sm: This week I worked more on consolidating project scripts, porting make rules to just. Also participated in timeclock discussions, did some research and brainstormed next steps.
nobodyinperson: I published annextimelog v0.3.1, a git-annex based time tracker (alpha quality, bare minimum functionality), which can export its data to hledger-readable timeclock.
https://pypi.org/project/annextimelog
Robert Nielsen: On the hledger fan website I look at using desc as a timesaver, and why this matters: https://hledgerfan.com/a-better-description
Misc: ShrykeWindgrace’s PR upgrading hledger-ui to the new brick and vty libs has landed; hledger-ui now runs natively on Microsoft Windows.
Quotes of the week: I’ve been using hledger for a few years. … Every service lets you download historical data as CSV, which can be imported by hledger and turned into journal files and then into visual reports. … It’s a bespoke setup and super nerdy and is a bit of a pain, but it’s literally the only solution that works for us.
This Week In Hledger 2023-12-15
Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with “TWIH”).
sm: Bugfixing
- Issue triage, ISSUES > Prioritising doc, dashboard, new issue template updates
- hledger-web: fix –base-url, drop –file-url, better startup messages, improve tests, code cleanups, manual cleanups, code docs.
Robert Nielsen: Discussion of the holiday season and hledger on the hledger fan website: https://hledgerfan.com/the-holiday-season-and-hledger
Misc: budget-cli - budget-focussed, CSV-centric, typescript PTA app with a good write-up
Quotes of the week: I switched, blissfully, away from Quicken/Quickbook/Xero/all that heavy crap to very simple and elegant textfile-based accounting (ledger/hledger) and it’s been the best and most flexible accounting experience I’ve ever had. I finally feel completely in control of my companies’ books.
This Week In Hledger 2023-12-08
Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with “TWIH”).
sm: Fixed a number of issues with 1.32 and released 1.32.1.
Rewrote the hledger manual’s “Budget report” section, and moved some content to the website’s Budgeting page.
Tried converting the bake bash scripts to just scripts.
Refactoring, issues, support.
Robert Nielsen: On the hledger fan website, this I discuss one of the most useful ledger mode commands for Emacs. Also, a book recommendation for graphic design that you need to know about: https://hledgerfan.com/upgrading-to-hledger-1-32-a-book-on-graphic-design-and-ledger-mode-clean-buffer
Quotes of the week: I’ve been researching the history of double entry bookkeeping alongside learning the basics of accounting (which is how all this started) and I think hledger and the whole universe of plain text accounting software is super cool. Just a lovely evolution of the method. Thanks for making it so welcoming and accessible
This Week In Hledger 2023-12-01
Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with “TWIH”).
sm:
Dev:
Added easy tagged time logging to timedot format, using letters instead of dots.
Ops:
Update the stars.hledger.org redirect (we have reached the top 30 Haskell projects by github stars 🌟 🎉).
Docs:
Use cli class instead of shell for all command-line examples in hledger docs.
hledger: Amount formatting, parseability
Ledger: interoperating tips updates, more about commodity directives and balance assertions.
Videos: reorder hledger fan videos.
REGRESSIONS: cleanups, discuss real names.
Other:
Prepared change notes and released hledger 1.32 with the last quarter’s changes.
I did a big cleanup on plaintextaccounting.org, which has absorbed the wiki and now supports Obsidian-style wiki links and clickable headings.
Robert Nielsen: Two things in this week’s hledger fan blog. First, for beginners, some notes on reducing errors when reconciling accounts. Second, for the more advanced, a recommendation for an Awk programming book. See the details at https://hledgerfan.com/fewer-errors-in-reconciling/.
Quotes of the week:
It’s the basis of my entire personal finance system.
once again, I was deeply impressed by how useful hledger is, and how thoughtful it is.
This Week In Hledger 2023-11-24
Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with “TWIH”).
sm:
Dev:
Fixes:
- Non-print-like reports no longer add trailing decimal marks (a regression in 1.31). Clarified the policy on number formatting adjustments made in certain reports/output formats. (#2115)
tag:queries now work when reading CSV files. (#2114)-o/--outputfilewith a .json or .sql extension now properly selects those formats.
Improvements/features:
- The
printcommand has a newbeancountoutput format for exporting to Beancount. - Timedot format supports a new letters syntax for easier tagged time logging. (#2116)
- Journal format now accepts lot costs with spaces after
{, improving Ledger compatibility.
Docs:
hledger: added “Amount formatting, parseability”, updated “Decimal marks, digit group marks”, “commodity directive”, “CSV > if table”, “Timedot”.
Ledger: added more notes on commodity directives, from chat.
Checking for errors: added tips on enforcing account lifetimes, from reddit, and on minimising diffs in reports, from mail list.
Cookbook: added “Reporting version control stats”.
Over at https://plaintextaccounting.org/#articles-blog-posts I started
adding blog links for 2021-2023, including 10 33 hledger-related posts
from Robert Nielsen, Jonathan Dowland, Arsen Arsenović, and Zoran Zaric.
Misc:
Updated project finance scripts, regenerated the project ledger with consistent precisions in assertions, updated it with the last few months of data from Open Collective.
Robert Nielsen: On the theme of the recent Thanksgiving Day, here are three ways you can use hledger to be thankful: https://hledgerfan.com/using-hledger-for-thanksgiving/.
Quotes of the week: Have been taking care of my (new) records for almost two years now and this data is really valuable for me. Gives me very nice overview over everything and confidence that everything is alright and so on. Just looking at some graphs can make you feel really good - in terms of “I know whats happening”, not necessarily in “all is well”, depending on those actual numbers! 😆 –someone using Beancount
This Week In Hledger 2023-11-17
Every Friday morning (or any time, tagged with “TWIH”): share your hledger/plain text accounting user notes, dev news, achievements or experiences in the hledger matrix chat.
sm:
Dev
A new hledger user pointed out in chat out that -s/–strict mode didn’t work with the import command. This exposed a bug (#2113) and led to some reworking of the API in Hledger.Read. Until now, when reading multiple files, -s/–strict checks were performed for each individual file, causing spurious failures, with import and other commands. Now strict checks are done only once, for the overall journal. Also, the import command now only updates .latest files at the end of a successful run (after successfully passing strict checks and after updating the journal file).
Post-“Precisiongeddon” followup: updating hledger-ui and hledger-web to show amounts with correct display precision.
Ops
Fixed a problem with cloudflare authentication that was preventing automatic TLS certificate renewal on hledger.org.
Clarified and documented my cloudflare tokens, and moved them to env vars so the latest caddy config could be committed.
Fixed the hledger.org webhook that was not updating the site on git push.
Enabled https for “code.hledger.org” and “site.hledger.org” short urls.
Fixed some caddy warnings, formatted the config file, added some caddy/webhook management scripts to the site makefile.
Docs
Spent some time drafting alternate hledger manual structures and thinking about adding a user guide.
Misc
Time breakdown this week: 52% enhancement, 35% cleanup, 13% support.
Robert Nielsen: On the hledger fan website, I discuss using the aregister and print commands to get narrow and wider views of your data: https://hledgerfan.com/zooming-out-and-in
Quotes of the week:
Great software btw, I appreciate your work! Been poking around with PTA and hledger since I was laid off, and it’s been incredible for managing/extending my runway 😁 I was a Mint/Personal Capital power user before, but this takes it to the next level. Got a new job and I’ve already forecasted out the whole next year, very handy and something I could never do before“
I spent some time trying to set up hledger so that I’d actually use it. With multiple accounts it really got tedious but I loved the actual double entry accounting and level of detail. My wife was blown away by the year end summary I made with it. You can see early how much went to taxes, fica etc etc.
I’m really happy how my hledger edit addon helps me quickly reconcile transactions. After CSV-importing from all different sources (phone notes, bank websites, etc.), I pile up the ridiculous amount of invoices and receipts next to me, grab one after the other and execute hledger edit amt:35.11 or hledger edit Checking desc:ALDI for example to find and edit the transaction in my $EDITOR, no matter where that txn was located originally. The other way round (going through the journal, then finding the physical invoice/receipt) turned out to be much slower.
I learned one thing: if %FIELDNAME REGEX matches against FIELDNAME
in the CSV. I didn’t know that! I always match against the whole line
and am annoyed if the order of columns makes this weird
This Week In Hledger 2023-11-10
It’s time for This Week In Hledger! Every Friday morning (or any time, tagged with “TWIH”), share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat.
sm:
Dev
This week I completed the months-long yak shave that became Precisiongeddon, and it has landed in master; see https://github.com/simonmichael/hledger/pull/2111 for details. Heads up: this can change default precisions shown especially by cost and value reports - all for the better hopefully, but any pre-release testing is welcome. There’s a linux binary at https://github.com/simonmichael/hledger/actions/runs/6804488282.
CI (continuous integration) workflows on github have been optimised a bit:
- Scheduled weekly builds have been disabled, as they were propagating to forks and running wastefully there in some cases.
- Some repeated rebuilding of the hledger-lib and hledger packages that seems unnecessary has been stopped.
- hledger-ui no longer builds its modules twice.
- Haddock testing has been moved to the release workflows to save time.
Fixed:
- amounts in value reports can sometimes be shown unstyled / with zero decimal digits, https://github.com/simonmichael/hledger/issues/2105
- auto postings break redundant equity/cost detection and transaction balancing, https://github.com/simonmichael/hledger/issues/2110
My time log for the week shows 69% enhancement, 27% cleanup, 5% support.
Docs
As part of Precisiongeddon, I started expanding hledger’s “code docs”, in the Hledger module’s haddock.
I spent time learning how to navigate Haddock and producing a fast-feedback authoring workflow (make haddock-watch).
And documented a bunch of “jargon” terms, and how we handle precision and display styles.
The “Regular expressions” manual section has been rewritten and now has examples: https://hledger.org/dev/hledger.html#regular-expressions.
Unmentioned last week: I added a News page on hledger.org as a stable home for This Week In Hledger and other news updates.
Misc
Some notable commits:
- feat: cli: Add tsv output (#869) (Peter Sagerson)
- feat: import: interpolate regex matches in field templates (#2009) (Jonathan Dowland)
There’s new interest and design discussion for referencing the matched account name in auto postings, and I have added to lukasbestle’s $20 bounty to make it $100: https://github.com/simonmichael/hledger/issues/1975
Robert Nielsen: For more advanced hledger aficionados, here is a diagram showing just about every possible part of an hledger transaction: https://hledgerfan.com/almost-everything-you-wanted-to-know-about-hledger-transactions.
Quotes of the week:
Overall very happy with hledger so far btw!
And thanks for the amazing tool. Migrating to hledger has been one of the most satisfying bits of digital homesteading I’ve done.
This Week In Hledger 2023-11-03
sm:
Dev
- tests: add outshine headings for folding/readability (in Emacs), readme updates, include .test files in TAGS, clean up ledger-compat tests
- tools: bake dbgstrs - print a bunch of hledger debug messages for review
- tools: ghci :rmain - like :main, but reloads first, useful eg when changing –debug level
- WIP: imp: prices: fixes, clarify/improve semantics
- WIP: precisiongeddon: precision/rounding cleanups/fixes/tests/docs, too intricate to describe
Continuing heavy work on consistent and optimal precision handling, and all the related yaks which keep showing up.
Seen
- https://www.chvp.be/blog/nanoledger/ nice blog post introducing nanoledger android app
Robert Nielsen: Beginner’s corner: What are the parts of a basic hledger transaction? Two resources for learners: https://hledgerfan.com/beginners-corner-a-basic-hledger-transaction
This Week In Hledger 2023-10-27
sm:
Dev
- demo: avoid a bug in asciinema 2.3.0, and improve the error message when asciinema fails (#2094)
- web: fix broken “File format help” link in the edit form (#2103)
- web: access control UX cleanups: replace –capabilities and –capabilities-header with –allow, and validate it before starting the app (#834)
- web: refactor permission checking
- web: sandstorm web app cleanups; rename/reorder roles & permissions
- WIP: testing and improving amount style and precision handling in cost/value reports
Ops
- Set up a self-hosted Sandstorm server and learned how to configure it.
- Set up a public hledger-web instance in it: sandbox.hledger.org. Unlike https://demo.hledger.org, and until the spammers find it, sandbox is fully writable - use it as a pastebin for examples, eg.
Docs
- Contemplating: merging cookbook and examples/*; something more custom to replace mdbook
Examples
- hledger-report1: a custom compound report script, haskell and bash versions
Misc
- exploring latest Paisa, and how to use it with existing setups
Seen
-
https://www.reddit.com/r/plaintextaccounting/comments/17g0026/average_cost_capital_gains_with_hledger very clear tutorial posts on (one way of) tracking investments
-
The primary reason Ledger was created was because it maintains the double-entry accounting equation for you: all accounts must balance. GUI tools never did this aggressively enough, all of them having some kind of feature like “check accounts” that would end up finding unaccountable pennies here or there. –John Wiegley, https://www.reddit.com/r/plaintextaccounting/comments/17fg4oi/why_plain_text_accounting_over_tools_like_excel/
chvp: A new Android app, NanoLedger, was added to F-droid to facilitate adding transactions on the fly. Payees, notes and accounts can all be autocompleted. Give it a try at https://f-droid.org/en/packages/be.chvp.nanoledger/
Robert Nielsen: Why I created the hledger fan YouTube channel: https://hledgerfan.com/why-i-created-the-hledger-fan-youtube-channel
This Week In Hledger 2023-10-20
G’day @room. It’s time for This Week In Hledger ! Every Friday morning, if you have any user or dev news or experiences you’d like to share, post them in the hledger matrix chat (https://matrix.to/#/#hledger:matrix.org) as a message prefixed by “TWIH:”. Markdown formatting and edits are welcome. On Saturday I’ll publish on the hledger mail list and on mastodon.
sm: Changes merged this week include:
Dev
- print: add –round option for more control of precisions (#2085)
- balance-assigned amounts affect commodity styles again (#2091, regression in 1.30)
- timedot: parse unitful quantities more accurately (#2096)
- Jacob Weisz has updated the hledger-web app on Sandstorm (The amazing https://sandstorm.io cloud app platform that lets you set up free private hledger-web instances with a few clicks, check it out!)
Ops
- renamed main CI workflow and branch to “ci”
- updated and committed hledger.org’s caddy config and short urls (redirects)
- there’s now an easy short url for trying out the hledger-web app on Sandstorm: https://sandstorm.hledger.org.
Docs
- moved regression bounty info to https://hledger.org/REGRESSIONS.html
- hledger manual > Journal > Commodity display style: rewritten
- hledger manual > aregister, hledger-ui manual > Register screen: note how separately-dated postings get combined
Examples
- justfile: forecast-import: show but don’t consume future-dated forecast transactions
Robert Nielsen: https://hledgerfan.com/a-not-so-capital-idea-for-saving-time/ describes a timesaving tip for command line users. Specifically, when typing one of your account names, hledger “forgives” you if you don’t match the capitalization of an account name.
This Week In Hledger 2023-10-13
[hey there @room - I’d like to get a regular This Week In Hledger started, along the lines of This Week in Matrix (TWIM) … Having a regular news heartbeat like this, even a small one, will help build our community and momentum.]
Welcome to This Week In Hledger ! Every Friday morning, if you have any user or dev news or experiences you’d like to share, post them in the hledger matrix chat room as a message prefixed by “TWIH:”. Markdown formatting welcome, edits welcome. On Saturday I’ll publish on the hledger mail list and on mastodon. (Inspired by This Week In Matrix).
sm:
Merged
- feat: balance: Add only-summary flag (#1012) #2086 (Stephen Morgan)
- feat: CSV rule negation #2088 (bobobo1618)
- imp: web: include account declaration info in accounts JSON #2097 (S. Zeid)
WIP
- more control of display precision with
print - more consistent precision handling in general
- displaying zeros with consistent commodity symbol, precision etc. when possible
- bug wrangling
Docs
- How to record journal entries
- hledgermatic , a simple up-to-date workflow
- Cleanups: Common workflows, Simon’s old setup
danielchappell:
What I like about the data format
- Double Entry accounting in human readable files, the journal files.
- Balance assignments (to check in on my cash-wallet occasionally)
- Virtual transactions (to track funds)
What I like about HLedger specifically
- static analysis cf. https://hledger.org/hledgermatic.html
What I need to do my homework on
- The limitations of balance assertions in HLedger
- study haskell
What makes me cry about HLedger
- A lack of amount expressions - github issue #183, https://hledger.org/ledger.html?highlight=expression#amount-expressions
Project accounting
Some ways to track small business/freelancer activity - orders, budgets, invoices, payments..
Accrual method
Revenue is declared when work is performed:
; budget:* - virtual accounts tracking what customers have committed
; to pay for various things. Should not go below 0.
2017/10/30 Order from CUSTOMER (order id)
(budget:CUSTOMER:PROJECT_ID:pos1) 1000
(budget:CUSTOMER:PROJECT_ID:pos2) 3000
; some work was done on pos1 and pos2, invoice for it.
; Using accrual accounting method
; (revenue is declared when work is done, ~= when invoiced)
2017/10/31 Invoice (invoice id) - (PROJECT_ID)
(budget:CUSTOMER:PROJECT_ID:pos1) -500 ; update project budget
(budget:CUSTOMER:PROJECT_ID:pos2) -1000
assets:receivable:CUSTOMER:PROJECT_ID:pos1 500
assets:receivable:CUSTOMER:PROJECT_ID:pos2 1000
revenues:CUSTOMER
(liabilities:tax:federal) -150 ; note tax due, eg 15% of revenue
; a customer payment is received
2017/11/15 Payment for INVOICE_ID
assets:receivable:CUSTOMER:PROJECT_ID:pos1 -500
assets:receivable:CUSTOMER:PROJECT_ID:pos2 -1000
assets:bank:checking
; make a tax payment
2018/4/15 Pay taxes due from 2017
liabilities:tax:federal 5000
assets:bank:checking
Cash method
Revenue is declared when payment is received:
2017/10/30 Order from CUSTOMER (order id)
(budget:CUSTOMER:PROJECT_ID:pos1) 1000
(budget:CUSTOMER:PROJECT_ID:pos2) 3000
; record an invoice sent. Not a real transaction in cash accounting,
; but we can balance it with the project budget as shown:
2017/10/31 Invoice (invoice id) - (PROJECT_ID)
budget:CUSTOMER:PROJECT_ID:pos1 -500
assets:receivable:CUSTOMER:PROJECT_ID:pos1 500
budget:CUSTOMER:PROJECT_ID:pos2 -1000
assets:receivable:CUSTOMER:PROJECT_ID:pos2 1000
; receive payment. Cash basis, so revenue declared here.
2017/11/15 Payment for INVOICE_ID
(assets:receivable:CUSTOMER:PROJECT_ID:pos1) -500
(assets:receivable:CUSTOMER:PROJECT_ID:pos2) -1000
revenues:CUSTOMER -1500
(liabilities:tax:federal) -150 ; note tax due, eg 15% of revenue
assets:bank:checking
; make a tax payment
2018/4/15 Pay taxes due from 2017
liabilities:tax:federal 5000
assets:bank:checking
Report examples
There are some example reports on the home page.
Here are some more example reports using the bcexample journal from beancount:
A yearly income statement, summarised to depth 3, sorted by amount:
$ hledger -f examples/bcexample.hledger is -Y -3 -S
Income Statement 2012-01-01..2014-10-11
|| 2012 2013 2014
================================++==============================================================================================================================
Revenues ||
--------------------------------++------------------------------------------------------------------------------------------------------------------------------
Income:US:Federal || 17000.00 IRAUSD 17500.00 IRAUSD 17500.00 IRAUSD
Income:US:Hoogle || 129132.20 USD, 120.12 VACHR 129382.20 USD, 120.12 VACHR 106183.70 USD, 97.02 VACHR
Income:US:ETrade || 0 114.42 USD 258.92 USD
--------------------------------++------------------------------------------------------------------------------------------------------------------------------
|| 17000.00 IRAUSD, 129132.20 USD, 1 more.. 17500.00 IRAUSD, 129496.62 USD, 1 more.. 17500.00 IRAUSD, 106442.62 USD, 1 more..
================================++==============================================================================================================================
Expenses ||
--------------------------------++------------------------------------------------------------------------------------------------------------------------------
Expenses:Home:Rent || 28800.00 USD 28800.00 USD 21600.00 USD
Expenses:Food:Restaurant || 4517.54 USD 4286.23 USD 4164.76 USD
Expenses:Food:Groceries || 2188.90 USD 2222.97 USD 1602.51 USD
Expenses:Transport:Tram || 1320.00 USD 1320.00 USD 1080.00 USD
Expenses:Health:Vision || 1099.80 USD 1099.80 USD 888.30 USD
Expenses:Home:Internet || 960.48 USD 959.82 USD 720.50 USD
Expenses:Home:Electricity || 780.00 USD 780.00 USD 585.00 USD
Expenses:Health:Medical || 711.88 USD 711.88 USD 574.98 USD
Expenses:Health:Life || 632.32 USD 632.32 USD 510.72 USD
Expenses:Financial:Commissions || 35.80 USD 214.80 USD 89.50 USD
Expenses:Health:Dental || 75.40 USD 75.40 USD 60.90 USD
Expenses:Financial:Fees || 48.00 USD 48.00 USD 40.00 USD
Expenses:Food:Coffee || 16.28 USD 19.79 USD 47.65 USD
Expenses:Food:Alcohol || 0 22.35 USD 0
Expenses:Taxes:Y2013 || 0 17500.00 IRAUSD, 51477.20 USD 859.09 USD
Expenses:Taxes:Y2014 || 0 0 17500.00 IRAUSD, 41836.20 USD
Expenses:Taxes:Y2012 || 17000.00 IRAUSD, 51477.20 USD 917.43 USD 0
--------------------------------++------------------------------------------------------------------------------------------------------------------------------
|| 17000.00 IRAUSD, 92663.60 USD 17500.00 IRAUSD, 93587.99 USD 17500.00 IRAUSD, 74660.11 USD
================================++==============================================================================================================================
Net: || 36468.60 USD, 120.12 VACHR 35908.63 USD, 120.12 VACHR 31782.51 USD, 97.02 VACHR
Yearly balance sheets, with commodities converted to their year-end value in USD where possible:
$ hledger -f examples/bcexample.hledger bs -Y -V --infer-value
Balance Sheet 2012-12-31..2014-12-31, valued at period ends
|| 2012-12-31 2013-12-31 2014-12-31
============================++=====================================================================================
Assets ||
----------------------------++-------------------------------------------------------------------------------------
Assets:US:BofA:Checking || 7448.62 USD 7247.12 USD 596.05 USD
Assets:US:ETrade:Cash || 337.18 USD 239.06 USD 5120.50 USD
Assets:US:ETrade:GLD || 0 6213.90 USD 6592.60 USD
Assets:US:ETrade:ITOT || 1364.90 USD 4021.92 USD 2648.26 USD
Assets:US:ETrade:VEA || 1411.56 USD 2414.28 USD 4290.84 USD
Assets:US:ETrade:VHT || 4695.80 USD 5456.00 USD 14782.32 USD
Assets:US:Hoogle:Vacation || 120.12 VACHR 240.24 VACHR 337.26 VACHR
Assets:US:Vanguard:Cash || -0.04 USD 0 -0.02 USD
Assets:US:Vanguard:RGAGX || 15595.79 USD 28532.66 USD 41391.57 USD
Assets:US:Vanguard:VBMPX || 10037.03 USD 22449.53 USD 33769.05 USD
----------------------------++-------------------------------------------------------------------------------------
|| 40890.84 USD, 120.12 VACHR 76574.46 USD, 240.24 VACHR 109191.17 USD, 337.26 VACHR
============================++=====================================================================================
Liabilities ||
----------------------------++-------------------------------------------------------------------------------------
Liabilities:US:Chase:Slate || 1366.52 USD 1906.01 USD 2891.85 USD
----------------------------++-------------------------------------------------------------------------------------
|| 1366.52 USD 1906.01 USD 2891.85 USD
============================++=====================================================================================
Net: || 39524.32 USD, 120.12 VACHR 74668.45 USD, 240.24 VACHR 106299.32 USD, 337.26 VACHR
The same reports as HTML:
$ hledger -f examples/bcexample.hledger is -Y -3 -S -o is.html
$ hledger -f examples/bcexample.hledger bs -Y -V --infer-value -o bs.html
$ open is.html bs.html
Income Statement 2012-01-01..2014-10-11 | |||
|---|---|---|---|
| 2012 | 2013 | 2014 | |
| Revenues | |||
| Income:US:Federal | 17000.00 IRAUSD | 17500.00 IRAUSD | 17500.00 IRAUSD |
| Income:US:Hoogle | 129132.20 USD, 120.12 VACHR | 129382.20 USD, 120.12 VACHR | 106183.70 USD, 97.02 VACHR |
| Income:US:ETrade | 0 | 114.42 USD | 258.92 USD |
| Total: | 17000.00 IRAUSD, 129132.20 USD, 120.12 VACHR | 17500.00 IRAUSD, 129496.62 USD, 120.12 VACHR | 17500.00 IRAUSD, 106442.62 USD, 97.02 VACHR |
| Expenses | |||
| Expenses:Home:Rent | 28800.00 USD | 28800.00 USD | 21600.00 USD |
| Expenses:Food:Restaurant | 4517.54 USD | 4286.23 USD | 4164.76 USD |
| Expenses:Food:Groceries | 2188.90 USD | 2222.97 USD | 1602.51 USD |
| Expenses:Transport:Tram | 1320.00 USD | 1320.00 USD | 1080.00 USD |
| Expenses:Health:Vision | 1099.80 USD | 1099.80 USD | 888.30 USD |
| Expenses:Home:Internet | 960.48 USD | 959.82 USD | 720.50 USD |
| Expenses:Home:Electricity | 780.00 USD | 780.00 USD | 585.00 USD |
| Expenses:Health:Medical | 711.88 USD | 711.88 USD | 574.98 USD |
| Expenses:Health:Life | 632.32 USD | 632.32 USD | 510.72 USD |
| Expenses:Financial:Commissions | 35.80 USD | 214.80 USD | 89.50 USD |
| Expenses:Health:Dental | 75.40 USD | 75.40 USD | 60.90 USD |
| Expenses:Financial:Fees | 48.00 USD | 48.00 USD | 40.00 USD |
| Expenses:Food:Coffee | 16.28 USD | 19.79 USD | 47.65 USD |
| Expenses:Food:Alcohol | 0 | 22.35 USD | 0 |
| Expenses:Taxes:Y2013 | 0 | 17500.00 IRAUSD, 51477.20 USD | 859.09 USD |
| Expenses:Taxes:Y2014 | 0 | 0 | 17500.00 IRAUSD, 41836.20 USD |
| Expenses:Taxes:Y2012 | 17000.00 IRAUSD, 51477.20 USD | 917.43 USD | 0 |
| Total: | 17000.00 IRAUSD, 92663.60 USD | 17500.00 IRAUSD, 93587.99 USD | 17500.00 IRAUSD, 74660.11 USD |
| Net: | 36468.60 USD, 120.12 VACHR | 35908.63 USD, 120.12 VACHR | 31782.51 USD, 97.02 VACHR |
Balance Sheet 2012-12-31..2014-12-31, valued at period ends | |||
|---|---|---|---|
| 2012 | 2013 | 2014 | |
| Assets | |||
| Assets:US:BofA:Checking | 7448.62 USD | 7247.12 USD | 596.05 USD |
| Assets:US:ETrade:Cash | 337.18 USD | 239.06 USD | 5120.50 USD |
| Assets:US:ETrade:GLD | 0 | 6213.90 USD | 6592.60 USD |
| Assets:US:ETrade:ITOT | 1364.90 USD | 4021.92 USD | 2648.26 USD |
| Assets:US:ETrade:VEA | 1411.56 USD | 2414.28 USD | 4290.84 USD |
| Assets:US:ETrade:VHT | 4695.80 USD | 5456.00 USD | 14782.32 USD |
| Assets:US:Hoogle:Vacation | 120.12 VACHR | 240.24 VACHR | 337.26 VACHR |
| Assets:US:Vanguard:Cash | -0.04 USD | 0 | -0.02 USD |
| Assets:US:Vanguard:RGAGX | 15595.79 USD | 28532.66 USD | 41391.57 USD |
| Assets:US:Vanguard:VBMPX | 10037.03 USD | 22449.53 USD | 33769.05 USD |
| Total: | 40890.84 USD, 120.12 VACHR | 76574.46 USD, 240.24 VACHR | 109191.17 USD, 337.26 VACHR |
| Liabilities | |||
| Liabilities:US:Chase:Slate | 1366.52 USD | 1906.01 USD | 2891.85 USD |
| Total: | 1366.52 USD | 1906.01 USD | 2891.85 USD |
| Net: | 39524.32 USD, 120.12 VACHR | 74668.45 USD, 240.24 VACHR | 106299.32 USD, 337.26 VACHR |
Reporting version control stats
You can export version control history as a journal, for analysis with hledger. Here’s an example: comparing the big 3 PTA apps’ git commit counts.
Start a journal with some account declarations; this is just to set the projects’ display order:
printf "account ledger\naccount hledger\naccount beancount\n" >project-commits.j
Export each git commit as a transaction. git log makes this easy; we include the commit date, short hash, and summary:
for P in ledger hledger beancount; do
git -C ../$P log --reverse --format="%cd (%h) %s%n ($P) 1%n" --date=short
done >> project-commits.j
The resulting journal looks like:
account ledger
account hledger
account beancount
2003-09-29 (e95ea133) Initial revision
(ledger) 1
2003-09-29 (faaed980) *** empty log message ***
(ledger) 1
...
Now we can run reports:
$ hledger -f project-commits.j stats
Main file : /Users/simon/src/PTA/hledger/project-commits.j
Included files :
Transactions span : 2003-09-29 to 2023-12-02 (7369 days)
Last transaction : 2023-12-01 (0 days ago)
Transactions : 24966 (3.4 per day)
Transactions last 30 days: 76 (2.5 per day)
Transactions last 7 days : 15 (2.1 per day)
Payees/descriptions : 19997
Accounts : 3 (depth 1)
Commodities : 1 ()
Market prices : 0 ()
Run time (throughput) : 0.70s (35759 txns/s)
$ hledger -f project-commits.j bal -YTN --transpose
Balance changes in 2003-01-01..2023-12-31:
|| ledger hledger beancount
=========++============================
2003 || 50 0 0
2004 || 327 0 0
2005 || 0 0 0
2006 || 0 0 0
2007 || 0 150 0
2008 || 1248 407 190
2009 || 1462 610 86
2010 || 437 606 15
2011 || 153 529 25
2012 || 706 350 18
2013 || 708 365 433
2014 || 578 567 1484
2015 || 367 542 1160
2016 || 115 732 686
2017 || 55 892 240
2018 || 78 1155 393
2019 || 130 1089 189
2020 || 90 1313 773
2021 || 42 1131 299
2022 || 55 832 112
2023 || 162 773 57
Total || 6763 12043 6160
$ for P in ledger hledger beancount; do hledger -f project-commits.j reg -w80 ^$P | head -1; done
2003-09-29 Initial revision (ledger) 1 1
2007-01-27 start (hledger) 1 1
2008-04-23 Initial import. (beancount) 1 1
$ for P in ledger hledger beancount; do hledger -f project-commits.j print ^$P | hledger -f- bal -YATN --summary | tail -4; done
|| Total Average
========++==================
ledger || 6763 322
|| Total Average
=========++==================
hledger || 12043 708
|| Total Average
===========++==================
beancount || 6160 385
Rewrite account names
Here’s an example of using account aliases.
Say a sole proprietor has a personal.journal:
2014/1/2
expenses:food $1
assets:cash
and a business.journal:
2014/1/1
expenses:office supplies $1
assets:business checking
So each entity (the business owner, and the business) has their own file with its own simple chart of accounts.
However, at tax reporting time we need to view these as a single entity (at least in the US).
In unified.journal, we include both files, and rewrite the personal
account names to fit into the business chart of accounts,
alias expenses = equity:draw:personal
alias assets:cash = assets:personal cash
include personal.journal
end aliases
include business.journal
Now we can see the data from both files at once, and the personal account names have changed:
$ hledger -f unified.journal print
2014/01/01 # from business.journal - no aliases applied
expenses:office supplies $1
assets:business checking $-1
2014/01/02 # from personal.journal
equity:draw:personal:food $1 # <- was expenses:food
assets:personal cash $-1 # <- was assets:cash
You can also specify aliases on the command line. This could be useful to quickly rewrite account names when sharing a report with someone else, such as your accountant:
$ hledger --alias 'my earning=income:business' ...
See also Change account name separator.
Rewrite commodity symbols
Three ways to temporarily change a commodity symbol, eg to show “$” as “USD” in a report:
Postprocess
Simplest.
Here, we use sed to replace all $ with USD in the output:
$ hledger bal | sed 's/\$/USD /g'
Preprocess
Most powerful. We rewrite the journal file before hledger processes it. You can merge multiple symbols into one this way, eg if inconsistent symbols have been used for a currency:
$ cat $LEDGER_FILE | sed 's/\$/USD /g' | hledger -f- bal
Value conversion
Most portable, requires only hledger. We create a dummy one-to-one market price between the old and new commodity symbols, and use market value reports to convert. This assumes your other market prices, if any, don’t interfere.
You can add the market price in the main journal:
$ echo 'P 2000-01-01 $ 1 USD' >> $LEDGER_FILE # once
$ hledger bal -V
Or in a separate file that you include only when needed:
$ echo 'P 2000-01-01 $ 1 USD' >> rewrite-symbols.j # once
$ hledger bal -V -f $LEDGER_FILE -f rewrite-symbols.j
Save frequently used options
You can save frequently used options and arguments in an argument file, one per line, then reuse them via a @FILE argument on the command line.
Here’s an example.
I keep frequently-used options for quick daily reports in a file
called simple.args. The name can be anything; I use a .args suffix
so I can find these easily. Here’s the content of simple.args:
--alias=/:(business|personal):/=:
--alias=/:(bank|cash|online):/=:
--alias=/:bofi:/=:b
--alias=/:unify:/=:u
--alias=/:wf:/=:w
-2
cur:.
The format is one command-line flag or command-line argument per line. Special characters should have one less level of quoting than at the command prompt (so no quotes around those (|) characters), and spaces should be avoided except inside quotes (so = is used between flags and arguments).
Now if I write @simple.args in a hledger command line, it will be replaced
by all of the above options/flags.
(In case you’re wondering about this example: it removes some detail, giving simplified reports which were easier for me to read at a glance):
- the aliases simplify the chart of accounts, hiding some distinctions (eg business vs. personal) and flattening some bank account names
- the
-2depth flag limits account depth to 2, hiding deeper subaccounts - the
cur:.query argument shows only single-character currencies, hiding a bunch of cluttersome commodities I don’t want to see
Usage
Generate a balance report showing the simplified accounts:
$ hledger bal @simple.args
Start a live-updating hledger-ui showing the simplified asset accounts only:
$ hledger-ui --watch @simple.args assets
Options in the arguments file can be overridden by similar options later on the command line, in the usual way. Eg, to show just a little more account detail:
$ hledger bal @simple.args -3
Suppressing this feature
If you actually need to write an argument beginning with @,
eg let’s say you have an account pattern beginning with that character,
you’ll want a way to disable this feature. On unix systems at least,
you can do that by inserting a -- (double hyphen) argument first. Eg:
$ hledger bal @somewhere.com # looks for additional arguments in the ./somewhere.com file
$ hledger bal -- @somewhere.com # matches account names containing "@somewhere.com"
On windows, this double hyphen trick might require a hledger built with GHC 8.2+.
Scripting hledger
As a command line program, with plain text data formats, hledger is easy to extend and customise with scripts and add-on commands. This page describes some different kinds of scripting you can do around hledger.
For an overview of ready-made scripts you can install, see Scripts.
Scripts are programs which call other programs to get things done. They are often (though not always):
- small
- ready to run immediately with no compilation step
- quick to write and modify
- written by you to make your particular tasks easier.
They could be as simple a shell alias remembering the options for a common report, or a small program automating a more complex operation such as invoicing.
Shell aliases
Shell aliases are the simplest type of script. Their most common use is to save typing, as in the examples below. (These and most shell examples in these docs are for the popular Bash shell; translate appropriately if you use a different shell.)
# ~/finance/bashrc
alias acc='hledger accounts'
alias act='hledger activity'
alias add='hledger add'
alias areg='hledger aregister'
alias bal='hledger balance'
alias close='hledger close'
alias print='hledger print'
alias reg='hledger register'
alias stats='hledger stats'
alias tags='hledger tags'
alias bs='hledger bs'
alias bse='hledger bse'
alias is='hledger is'
alias cf='hledger cashflow'
alias 2020='hledger -f ~/finance/2020.journal'
alias 2019='hledger -f ~/finance/2019.journal'
alias 2018='hledger -f ~/finance/2018.journal'
# ...
alias 2y='hledger -f ~/finance/2year.journal'
alias 3y='hledger -f ~/finance/3year.journal'
alias 4y='hledger -f ~/finance/4year.journal'
alias 5y='hledger -f ~/finance/5year.journal'
alias all='hledger -f ~/finance/all.journal'
or to customise hledger’s commands:
alias iadd='hledger-iadd --date-format %Y/%m/%d'
alias ui='hledger-ui --watch'
alias uir='hledger-ui --watch --register'
or to remember useful reports:
alias allone="all --alias '/^([^:]+)(:personal|:business)\b/ = \1' --alias '/^revenues:consulting/=revenues' --alias '/\bwells fargo\b/=wf'"
alias budget='hledger bal --budget'
alias forecast='hledger reg -H date:today- --forecast'
alias tripactivity='hledger act tag:trip -W'
Shell functions
When aliases are too limiting, you can use functions instead:
alias bassets='bal -H ^assets:business'
alias bdebts='bal -H ^liabilities:business'
alias brevenues='bal ^revenues:business'
alias bexpenses='bal ^expenses:business'
function bsnapshot () { # business balance sheet + year-to-date income statement
echo Business financial snapshot `date` #+%Y/%m/%d
echo
echo Assets today:
bassets --flat --drop 2 --depth 4 $@
echo
echo Debts today:
bdebts --flat --drop 2 --depth 4 $@
echo
echo Revenues this year:
brevenues --flat --drop 2 --depth 3 $@
echo
echo Expenses this year:
bexpenses --flat --drop 2 --depth 3 $@
echo
}
Shell scripts
When aliases and functions are too limiting, you can use a shell script file:
#!/bin/sh
# ~/finance/bin/reports
date
export PATH=/Users/simon/.local/bin:$PATH
export LEDGER_FILE=/Users/simon/finance/2018.journal
/usr/bin/make -s -C /Users/simon/finance reports | /usr/bin/mail -s 'reports' simon
Make
Makefiles can work well for collections of simple scripts, especially if they are local to a particular directory.
# ~/finance/Makefile
# regular reports, mailed by cron
reports: \
is \
time-report \
todo \
is:
@echo 'Monthly Income Statement'
hledger is -M cur:.
@echo
YEAR:=$(shell date +%Y)
TIMEJOURNAL=time-$(YEAR).journal
HLEDGER=hledger
TIME=$(HLEDGER) --alias "/\./=:" -f $(TIMEJOURNAL)
time-report:
@echo 'Monthly Time Report'
$(TIME) bal -MAT -1
@echo
todo:
@echo "TODOs in this year's journals"
@grep TODO $(YEAR)*
@grep TODO $(YEAR)* | wc -l
@echo
If you are not comfortable with Make’s syntax, or when scripts get more complex, dedicated or multi-function shell scripts will be less hassle.
Just
just is a more modern alternative to make. I prefer it and recommend it for many use cases.
See hledger and just for more details.
Add-on commands
If you name scripts hledger-something and make them executable in PATH, they’ll be recognised as add-on commands.
Eg here’s a simple script which adds estimatedtax as a hledger command.
This one is in haskell, but it could be written in any language:
#!/usr/bin/env runhaskell
-- hledger-estimatedtax.hs
-- Given a gross income amount, estimate the corresponding federal and
-- state taxes based on historical tax per gross income. These
-- amounts can be posted to your tax liability account, to estimate
-- taxes due from this year's income so far.
import System.Environment
import Text.Printf
-- | How much of taxable income to save for federal & state estimated tax payments,
-- these are usually about right for me
fedrate = 0.28
staterate = 0.04
main = do
taxableincome <- fmap (read . head) getArgs :: IO Int
let fedtax = round $ fromIntegral taxableincome * fedrate
statetax = round $ fromIntegral taxableincome * staterate
totaltax = fedtax + statetax
posttaxincome = taxableincome - totaltax
printf (
"Taxable income: %5d\n" ++
"US tax: %5d\n" ++
"CA tax: %5d\n" ++
"Total tax: %5d\n" ++
"Post-tax income: %5d\n")
taxableincome fedtax statetax totaltax posttaxincome
hledger-lib scripts
These are scripts written in haskell, which use the same hledger-lib and hledger APIs that hledger uses. They can be simple interpreted scripts, or can grow as large and powerful as you need, and can be optionally compiled for more speed and durability. Example scripts and more tips can be found at https://github.com/simonmichael/hledger/tree/master/bin/.
hledger-lib scripts installed as add-ons (hledger-newcmd.hs in PATH)
are a good way to prototype new commands, perhaps to become builtin commands later.
(Though, converting a haskell script command to a builtin command currently requires
rearranging its code; we’d like to improve this.)
Haskell scripting tips
If you want others, or future-you, to be able to run your haskell scripts reliably, make them stack scripts or cabal scripts. stack scripts are usually best for reliability, performance, and sharing with others, because:
- they require only the
stackexecutable to be installed on your system - they ensure the required GHC version is installed, installing it automatically if needed
- they ensure the required haskell packages are installed, installing them automatically if needed
- they specify all package versions and use known-compatible packages for more reliable builds
- they automatically recompile themselves when changed, only when needed.
Plugins
In general we would like for hledger users to be able to quickly customise its behaviour and features with small code “plugins”, without having to change and recompile hledger itself. Haskell is not a very “dynamically pluggable” language, so currently the best way to build “plugins” is with pre-processing scripts, post-processing scripts, or hledger-lib-using haskell scripts.
We identify several desirable plugin types:
| Plugin type | Purpose |
|---|---|
| reader | Parse/ingest from some data format/source to hledger journal data. |
| processor | Process/transform journal data, after parsing (ideally) and before reporting. |
| report | Calculate some kind of report data from processed journal data. |
| renderer | Render report data in some output format. |
| command | Provide new CLI subcommands implementing new reports/formats/actions. |
| writer | Write/export complete journal data to some data format/destination. |
Here are examples of each type of plugin, and the best approach for making your own. (More examples can be found at https://hledger.org/scripts.html or https://plaintextaccounting.org/#data-importconversion.)
reader
- the builtin journal, csv, timeclock, timedot readers
- beancount2ledger, pricehist, reckon, qb2ledger, ynab-to-ledger
A new reader can be made by generating journal or csv format for hledger to read, or with a hledger-lib script that builds hledger’s journal data structure.
processor
- the builtin amount/cost/equity inferring; amount style normalisation; auto postings; periodic transactions; default/strict checks..
hledger-print-location,hledger-swap-dates
Custom processing of hledger’s journal data structure, after parsing, is currently not well supported; it is somewhat possible in a hledger-lib script. As an alternative, you can transform the journal or csv textually before hledger reads it.
report
- the builtin reports: PostingsReport, AccountTransactionsReport, MultiBalanceReport, BudgetReport..
hledger-combine-balances- multi-report tools:
hledger-plot,hledger-vega, hreports, r-ledger
A report also needs a command to invoke it and a renderer to show it. An add-on script can provide all three of these.
renderer
- the report commands’ builtin output formats (usually txt, csv, json,.md..)
This can be done with an add-on script, or by transforming the report’s txt/csv/json/html output.
command
hledger-ui,hledger-web,hledger-iadd,hledger-git,hledger-move,hledger-edit,hledger-check-fancyassertions
A new subcommand can be invoked by hledger COMMAND ... and will appear in the hledger commands list.
It can be made with an add-on script or program (hledger-foo in PATH).
writer
- the
printcommand’s builtin output formats (txt, csv, json, sql) - ledger2beancount
A new writer can be made as a hledger-lib script, or (losing the original journal’s directives and top-level comments) by transforming print’s txt/csv/json/sql output.
SM’s 2019 setup
Some notes on my setup circa 2019 and later.
- Author: Simon Michael
- Tested with: mac mojave, hledger 1.14
Tools
hledger, hledger-ui, ledger-mode, GNU make, …
Files
$ tree ~/notes
/Users/simon/notes
├── 2019.journal
├── 2019.prices
├── all.journal
├── current.journal -> 2019.journal
├── forecast.journal
...
Files are in ~/notes.
There’s one YEAR.journal file per year containing all transactions in date order.
It includes two sub files:
- YEAR.prices containing P records for the year
- forecast.journal containing periodic transaction rules
all.journal includes all the year journals.
It provides all historical data, but is slow, and my old journals are inconsistent/broken, so it’s currently rarely used.
current.journal is a symlink for scripts which don’t know the year. Symbolic links are a mixed blessing, causing file path confusion in emacs, eg.
Scripts
I have convenience aliases for hledger commands and reports in ~/notes/bashrc,
which is automatically sourced by my bash profile.
Data fetching and report scripts are defined in ~/notes/Makefile.
Version control
Journal file, included files, makefile and scripts are tracked in git, with a mixture of manual and cron-based automatic committing.
Environment
The LEDGER_FILE environment variable is set to /Users/simon/notes/CURRENTYEAR.journal.
This is done in some super secret way that I’ll have to track down, or more likely several ways,
to ensure that it is consistent for:
- programs started from command line in iTerm/Terminal windows
- programs started from emacs shell buffers
- emacs modes such as ledger-mode
- for both text-mode and graphical emacs, whether started from command line or mac GUI (Dock/Finder/Spotlight)
- etc.
Accounts
I track both sole proprietor business accounts, with JS: prefix,
and personal accounts, with sm: prefix, in one journal for convenience.
These declarations ensure hledger knows the account types:
account JS:assets ; type:A
account JS:liabilities ; type:L
account JS:equity ; type:E
account JS:revenues ; type:R
account JS:expenses ; type:X
account sm:assets ; type:A
account sm:liabilities ; type:L
account sm:equity ; type:E
account sm:revenues ; type:R, taxable personal revenues
account sm:income ; type:R, already-taxed "salary" from JS, non-taxable income
account sm:expenses ; type:X
Getting data
Most journal entries are generated from downloaded CSV:
-
I fetch OFX from one bank with ledger-autosync (discontinued due to high fees) -
Transactions from three banks are aggregated and cleaned in a Google sheet by Tiller ($5/mo). I download this as CSV with agsheet-csvscript (via cron ?). (discontinued for security) -
Transactions are downloaded from banks by Plaid (free developer account). (discontinued for security) -
Paypal CSV is downloaded manually, then moved into place by a make rule. I use Paypal’s CSV because Tiller doesn’t handle multiple currencies and Paypal’s extra metadata fields. -
I download CSV manually from each bank, weeklyish.
-
I fetch CSV from Paypal’s API using
paypaljsonandpaypaljson2csvscripts.
CSV rules are never entirely finished; they get one or two small tweaks most times I import. They don’t cover 100% of my possible entries; a few that are too hard to generate just get generated partially, with a ’; TODO:` tag for me to fix them up manually. There aren’t many of those at this point
I fetch currency prices with barrucadu’s market-prices.py script (details).
Data entry (2021)
For transactions downloaded from banks:
- In an iterm3 tab titled FINANCE, I have a TUI emacs with two side-by-side windows.
- dIn the first window is 2021.journal, with ledger-mode and auto-revert-mode enabled.
- In the second window I switch to a shell and run make Import to download and import latest transactions.
- These show up in the journal right away. I switch focus there, select all the new entries and hit M-q to realign them with ledger-mode (which is better at that than hledger print).
- I process them one at a time from the top, marking each one cleared (C-c C-e) when it looks good.
- When there’s an unknown posting, I:
- replace the unknown account with the appropriate account (assuming I know it)
- switch to common.rules in the second window, and search for that account or its parent (assuming it’s in the rules somewhere)
- add some portion of the new entry’s description as a new pattern for this account. Or tweak the existing patterns so it will be matched next time (or to avoid over-matching by the wrong rule).
- when all new entries are marked cleared, I git commit the journal and any updated rules file(s). Or if I don’t, it will be auto-committed by a nightly cron job (in theory).
- I do this daily-ish, so it’s a small number of new txns each time.
For transactions which don’t appear in downloaded data (cash transactions, etc.):
- Note it somewhere, anywhere, as quickly possible, or it will be forgotten (and I’ll have to add an unexplained cash adjustment transaction later).
- If the laptop is out of reach, that will usually be a brief note on today’s page in a phone notes app (Obsidian, currently) or a paper notebook. Later that gets copied to the laptop.
- On the laptop, my usual routine is:
- get to the place of data entry
- iterm3 app (or start it if not running),
- FINANCE tab where a TUI emacs (client) is running (or create that tab/start that emacs client),
- emacs window showing the journal (or
C-j lif not showing), - end of buffer (
M->).
- add the journal entry: either
- if short of time: just a commented line, or date and description (and expand it later)
- if a familiar transaction: search back for a similar one, copy, jump to the end again, paste, adjust as needed (especially the date)
- otherwise type it in, starting with the date, using ledger-mode’s completion (
TAB), or hippie-expand’s completion (M-/) if they help. (ledger-mode required careful configuration for best behaviour with account names, the main thing I recall is turn onledger-complete-in-steps.) - when the journal entry is finalised, mark it cleared (
C-c C-e).
- get to the place of data entry
More error checking (2023)
I run make Import (equivalent to: make csv which fetches latest bank & paypal csvs, plus make import which essentially runs hledger import *.csv).
Then I review in emacs+ledger-mode+flycheck-mode. All the imported transactions are uncleared, showing up red. Flycheck also highlights problems like “expenses:unknown” or failing assertions or out of order dates. I clear each one, fixing (and maybe tweaking CSV rules) where needed.
The recentassertions check may force me to add a transaction with newer balance assertions for the main accounts; I describe that “reconcile”, I fill in the balances hledger expects, and at the same time I check they match the real-world balances.
Finally when all the red is gone and all checks are passing - enforced by adding this command to .git/hooks/precommit:
#!/bin/sh
...
# check main hledger journal
hledger check -s ordereddates recentassertions
…I commit the changes to git.
I have a nightly cron job which would do this automatically, but by habit and for clean commits I usually do them manually, with separate “txns”, “rules” and “scripts” commits. (If you try using cron like this, note failing checks can reveal account names and balances in the error message and cron might email those in plain text over the internet.)
For troubleshooting: when downloading a CSV the previous copy is saved as FILE.csv.old.
Cash transactions are entered in emacs, using ledger-mode. Mostly by copying and pasting similar past transactions.
When rewriting account names, I use either
ledger-mode completion (TAB) or dabbrev-expand completion (M-/),
which have different strengths.
SM’s 2023 setup
A relatively simple, automated workflow using current hledger features. The Files layout will give you the gist of it. It is a journal first setup (journal files are primary, CSV files are disposable).
Tools
hledger 1.30+ ,
just,
git (or other version control system),
…
Files
Here are the main files, grouped for clarity. These are unix-style file paths; on Windows, they may be a little different. These filenames are reasonably good for typing, autocompleting, visual grouping, editor configuration, etc. But you might find better ones.
/home/USER/
finance/ home of finance files; wherever you wish
.git/ most files are tracked with git or other VCS
hooks/
pre-commit runs hledger check -s ordereddates recentassertions
bin/ extra scripts/tools for finance work
bashrc extra shell config
gsheet-csv get a Google sheet as CSV
paypaljson get recent Paypal transactions as JSON
paypaljson2csv convert Paypal JSON to CSV
justfile commands/scripts for common tasks
2023.journal main journal file for 2023 (current year)
2023accounts.journal account declarations, included by main journal
2023prices.journal market price declarations, included by main
2022.journal main journal file for 2022 (etc..)
2022accounts.journal
2022prices.journal
forecast.journal future transactions / periodic transaction rules
bi-ichecking.csv.rules CSV conversion rules for each bank account
paypal.csv.rules
wf-pchecking.csv.rules
wf-bchecking.csv.rules
wf.rules common rules included for all wf accounts
common.rules common rules included by all
Downloads/ hledger looks here for CSV files by default;
these file names are declared in *.csv.rules
Checking1.csv a bank account's recent transactions
Checking1-1.csv newer copies saved by web browser
Checking1-2.csv the newest; hledger will read this one
Savings1.csv
Transactions-2342.csv
Scripts
justfile contains common reporting commands and task scripts, which can be listed and invoked by just1. I alias just to j.
The bin directory is a place to keep additional scripts and tools; it should be added to your shell’s PATH.
The scripts and an example justfile can be found at Scripts and add-ons.
When you want to add a custom script, you have a choice:
- you can define it as a shell alias or function in
bashrc - you can define it as an executable file in
bin - you can define it as a script in
justfile(or aMakefile, Shake file, or other build tool)
Over time, scripts can proliferate and become a maintenance and memory problem. I recommend using justfile where possible; it is the best combination of ease and power. I am increasingly using it as the starting point for all finance tasks.
Main journal
The main journal file is YEAR.journal. The LEDGER_FILE environment variable should be set to this.
It contains these sections, one after another:
- commodity declarations - for error checking and to set display styles/precision - recommended
- tag declarations - if you use tags, for error checking
includedirectives - to include other files. I try to keep files to a minimum, to reduce headaches, but certain things are convenient to keep in separate files, likeYEARaccounts.journalandYEARprices.journal.- (account aliases - I avoid permanently defined aliases, since they add complexity. But if you need them, define them here so they can affect everything.)
- transactions - from all accounts, in date order. New transactions can simply be appended at the end.
Accounts
Account declarations are kept in a separate YEARaccounts.journal, included by the main journal, for more convenient reviewing and updating. These provide error checking and declare account types, account display order, account tags and/or comments. New account declarations can be inserted in the correct place, or appended at the end.
Eg:
account assets ; type:A
account liabilities ; type:L
account equity ; type:E
account revenues ; type:R
account expenses ; type:X
account assets:bank ; type:C
account assets:cash ; type:C
account equity:conversion ; type:V
account assets:bank:checking
account assets:bank:savings
# etc...
Business accounts
As a sole proprietor, I like to track my business and my personal life as two separate entities. Legally we are one entity, and there are frequent transactions between them, so it’s convenient to keep them in the same journal. But I use a top-level entity prefix to keep the accounts clearly separated. I use two letters for these (less typing, less visual noise and keeps them aligned in reports). So my account declarations look like this:
; business 1
account JS:assets ; type:A
account JS:liabilities ; type:L
account JS:equity ; type:E
account JS:revenues ; type:R
account JS:expenses ; type:X
account JS:assets:bank ; type:C
account JS:assets:cash ; type:C
account JS:equity:conversion ; type:V
account JS:assets:bank:checking
account JS:assets:bank:savings
# etc...
; personal
account sm:assets ; type:A
account sm:liabilities ; type:L
account sm:equity ; type:E
account sm:revenues ; type:R, taxable personal revenues
account sm:income ; type:R, already-taxed "salary" from JS, non-taxable income
account sm:expenses ; type:X
account sm:assets:bank ; type:C
account sm:assets:cash ; type:C
account sm:equity:conversion ; type:V
account sm:assets:bank:checking
account sm:assets:bank:savings
# etc...
Prices
Market price declarations are also kept in a separate, included file, YEARprices.journal, in date order. New prices can be appended at the end. Eg:
# ...
P 2023-06-08 00:00:00 £ $1.25161
P 2023-06-08 00:00:00 ¥ $0.00719
P 2023-06-08 00:00:00 € $1.07676
P 2023-06-09 00:00:00 £ $1.25777
P 2023-06-09 00:00:00 ¥ $0.00717
P 2023-06-09 00:00:00 € $1.07713
P 2023-09-04 00:00:00 € $1.08021
# ...
forecast journal
forecast.journal is a place to note expected future transactions, one-time or recurring.
I include it in the main journal, and I put only periodic transaction rules (and a few auto posting rules) in it.
This means it has no effect until I run reports with the --forecast option.
Or you could put ordinary transaction entries here; in that case you may want to not include it, to keep future events out of reports by default.
CSV rules
Each CSV (or TSV, SSV, …) data source has a CSV rules file declaring how to convert it to meaningful journal entries. Each rules file declares its corresponding data file’s name, so that (eg) hledger import wf-pchecking.csv.rules will automatically look for the latest-numbered Checking1*.csv file in the ~/Downloads folder.
Some rules files are common rules included by the others, eg wf.rules and common.rules.
Downloads
~/Downloads is where my mac web browsers save downloaded CSV files, and it’s where the source rule looks for data files by default. If you don’t have this folder on your system, you can make it, or use a symbolic link; or you can specify a different folder in your source rules.
When downloading bank CSV files, you don’t need to care much about which dates you download; hledger’s import system will usually do the right thing. Just make sure to download enough data to cover the period since your last import (eg the last 30 days, last year, or all transactions).
You also don’t need to care much about managing downloaded CSV files; your browser will give them unique filenames, and hledger will automatically choose the latest ones. After successful import you can either delete the CSV files, keep them around for a while for troubleshooting, or archive them permanently.
Workflow
In the justfile I have a foo-import script for each data source foo, and the import script runs all of them. So it’s
- download one or more CSVs manually in web browser
j import --dryj import- load journal in emacs + ledger-mode + flycheck-hledger
- select new transactions,
M-qto align them - for each new transaction: review, refine and mark it cleared when appropriate.
- open flycheck’s error list with
C-c ! l, jump to and resolve each issue in turn. These will be things like typos, uncategorised postings, mis-ordered dates etc. - I also run the
recentassertionscheck, which periodically forces me to add more recent balance assertions for my main accounts. I do this by adding a “reconcile” transaction (usingyasnippet, by typing “reconcile” and TAB); filling in the balances hledger expects, to make the errors go away - When the journal is again error-free, I check each account’s real-world balance against the “reconcile” transaction’s asserted balance (or in a balance report or hledger-ui accounts screen), and resolve any disagreements.
- Finally I commit the changes to journal, rules and scripts. Git’s
pre-commithook runs my checks one more time, catching any last-minute errors.
-
justis Likemake, but better for this purpose. I recommend trying it, even though it doesn’t come with your OS; I say this after years of costly battle with make, shell, and other scripting setups. ↩
SM’s time & task dashboard
SM has invented a cool time and task-tracking aid. As you work on a mac computer (A), you wonder how long you have been here and what you should be doing instead. So you press Cmd-Shift-Return to call up your Time and Task Dashboard (B). The HotKey app (C) recognises your keypress and opens Terminal (D), which previously you positioned at top half of screen by pressing Ctrl-Opt-Cmd-Up to trigger a Hammerspoon window layout script (E). Inside the Terminal (D) is a text-mode Emacs (F) which you have split horizontally with Ctrl-x 3 into four side-by-side windows (G, H, I and J). In the first window (G), a vterm (K) is running a time script (L), which is defined in a justfile (M) and uses watchexec (N), cron (O), hledger (P) and various unix tools (Q) to show an updating time report (R). You review this to see where you have spent time today, your time goals if any, and how long it has been since you last updated the time log and since the computer woke up. Armed with this information you make any needed updates to your timedot-format time log (S), which is in the second window (H), usually narrowed to the current day. If you need to run reports or queries, you use the third window (I) which contains an emacs shell prompt (T). You have also been keeping more detailed notes and todo lists in a weekly notes file (U), named like 2023W51.md, with Obsidian (V). To see today’s goals and progress summarised, you glance at the fourth window (J), where a second vterm is running a todo script (W), also defined in justfile (M), showing an updating tasks report (X). This displays the list items from today’s section of the weekly notes file (U) with empty checkboxes in red, filled checkboxes as green check marks, and non-checkbox bullets as black squares. You review today’s green completed tasks, red uncompleted tasks and black unplanned activities. Reoriented and up to date, you press Cmd-Shift-Return again to hide the Terminal (D), and proceed with clarity and confidence.
time (L):
# Defined in ~/.bash_profile.local: $TIMELOG, $TIMEDATA
TIMEDIR := `dirname "$TIMELOG"`
TIMELOGALL := TIMEDIR / 'time-all.journal'
# This redisplays only when a file listed by `hledger -f $TIMELOG files` is modified.
# To force a per minute display as well, have $TIMELOG include a dummy file (.tick)
# and configure a cron job to touch that every minute.
# (This is better than touching the timelog file itself, which confuses editors.)
#
# show time status, redisplaying when timelog files change
time *ARGS:
#!/usr/bin/env bash
set -euo pipefail
cd "$TIMEDIR"
opts= #--poll=10 # <- uncomment to fix symlinked files being ignored
$WATCHEXEC $opts --no-vcs-ignore \
--filter-file=<(hledger -f "$TIMELOG" files | sed -E "s|$TIMEDIR/||g") \
-c -r just time1 "$@"
# show time status, redisplaying every minute with watch
# dash-1m *ARGS:
# watch -n60 -c tt status
# }
# show current time status
time1 *ARGS:
#!/usr/bin/env bash
# XXX changing the above to osh causes tdash to hang
set -euo pipefail
date=$(if [ "$(builtin type -p gdate)" ]; then echo gdate; else echo date; fi)
stat=$(if [ "$(builtin type -p gstat)" ]; then echo gstat; else echo stat; fi)
curtime=$($date +'%H:%M %Z, %a %b %-e %Y')
modtime=$($date +'%H:%M %Z' -r "$TIMEDATA")
modsecs=$($stat -c %Y "$TIMEDATA")
nowsecs=$($date +%s)
agesecs=$((nowsecs - modsecs))
agemins=$(python3 -c "print($agesecs/60)")
agehrs=$(python3 -c "print($agesecs/3600.0)")
ageqtrhrs=$(python3 -c "print(round($agesecs/900.0))")
agedots=$(just tdots "$ageqtrhrs")
printf "Current time: %s\n" "$curtime"
# old, for osh: use env here to run the system printf, which supports floating point
env printf "Timelog saved: %s, %.0fm / %.1fh / %s ago\n" "$modtime" "$agemins" "$agehrs" "$agedots"
# Show the current day/week/month budget status.
printf "Time plans:\n"
# calculate each period's budget from daily budget
hledger -f "$TIMELOG" bal -1 -p 'daily today' --budget=Daily -E "$@" | tail +2
# hledger -f "$TIMELOG" bal -1 -p 'weekly this week' --budget=Daily -E "$@" | tail +2
# hledger -f "$TIMELOG" bal -1 -p 'monthly this month' --budget=Daily -E "$@" | tail +2
# or use each period's specific budget
# hledger -f "$TIMELOG" bal -p 'daily today' --budget=Daily -1 | tail +2
# hledger -f "$TIMELOG" bal -p 'weekly this week' --budget=Weekly -1 | tail +2
# hledger -f "$TIMELOG" bal -p 'monthly this month' --budget=Monthly -1 | tail +2
echo
hledger -f "$TIMELOG" check -s tags ordereddates || true
# this comes last because it's slow and variable length
echo
printf "Display activity:\n"
wakelog today | tail -n 6
todo (W):
# keep these synced with obsidian:
WEEKNOTES := '~/notes/CLOUD/`date +%YW%V`.md'
TODAYHEADING := '^## ' + `date +'%a %-d'`
# print the path of this week's notes file
@weeknotes:
echo {{ WEEKNOTES }}
# watch today's tasks, with any rg options (like -c)
@todo *OPTS:
if [[ ! -f {{ WEEKNOTES }} ]]; then echo "{{ WEEKNOTES }} does not exist yet"; exit 1; fi
$WATCHEXEC -w {{ WEEKNOTES }} -- 'clear; just todo1 {{ OPTS }}'
# list tasks in today's notes (checkboxes and other bullet items)
@todo1 *OPTS:
if [[ ! -f {{ WEEKNOTES }} ]]; then echo "{{ WEEKNOTES }} does not exist yet"; exit 1; fi
awk '/^#+ Inbox/{f=0};f;/{{ TODAYHEADING }}/{f=1}' {{ WEEKNOTES }} \
| sed 's/\[x]/✅/' \
| gsed -E 's/- (\w)/- ◼️ \1/' \
| rg {{ OPTS }} '^ *\- (\[[ x]\])?' \
# list tasks in this week's notes
@todo-week *OPTS:
rg {{ OPTS }} '^ *\- (\[[ x]\])?' {{ WEEKNOTES }}
wakelog:
#!/usr/bin/env bash
# Show notable mac sleep/wake events, trimmed to terminal width
# for tidy display, eg by watch.
# Requires: cut grep sed tput
# XXX slow
usage() {
cat <<EOF
Show notable mac sleep/wake events, trimmed to terminal width.
Commands:
wakelog today - show main sleep/wake events so far today
wakelog yesterday - show main sleep/wake events yesterday
wakelog list - show main recent sleep/wake events
wakelog all - show all recent sleep/wake events
EOF
# ^ keep synced with functions below
}
# XXX in some cases this might not work, then the log lines are not cut,
# and their long trailing whitespace can cause lines to wrap.
width=$(tput cols)
grep="grep -E"
grep="rg --color=never"
today() {
list | $grep "$(date +^%Y-%m-%d)"; true
}
yesterday() {
list | $grep "$(date -v-1d +^%Y-%m-%d)"; true
}
listv() { # list simplified sleep/wake/display off/on events from mac power event log
pmset -g log | $grep '((Sleep|Wake) +|Display is)' \
| sed -E \
-e "s/\t/ /g" \
-e 's/(Notification|Sleep|Wake) * //' \
-e 's/is turned //' \
-e 's/Entering Sleep state/Sleep/' \
| $grep -v '(DarkDarkWake|Maintenance Sleep|Back to Sleep)' \
| cut -c 1-"$width" # clip long lines
}
list() { # list terse display off/on events from mac power event log
pmset -g log | rg '^(...................).*Display is turned (.*)' -or'$1 $2' \
| sed -E \
-e "s/\t/ /g" \
| cut -c 1-"$width"
}
all() {
pmset -g log \
| $grep -e " Sleep " -e " Wake " -e " DarkWake " -e " Wake Requests " \
| sed -E -e "s/\t/ /g" \
| cut -c 1-"$width"
}
if declare -f "$1" > /dev/null; then "$@"; else usage; fi
Tags tutorial
Author: Robert Nielsen (YouTube: hledger fan) with invaluable input from Simon Michael
Don’t be scared away from using tags in your hledger accounting this Halloween, or any other time for that matter. This is a tutorial on using tags with hledger, so if the idea of using tags has been haunting you, but you are not really sure how to use them, read on.
Let’s start with a file showing someone’s hledger accounting around the time of Halloween:
2022/09/25 ACME Costume
Expenses:Entertainment $45.99 ; Amoeba Man
Liabilities:CreditCard
2022/10/30 Cheap-O-Gas
Expenses:Automotive $37.15
Liabilities:CreditCard
2022/11/01 Medical Associates
Expenses:Medical $80.00 ; upset stomach
Liabilities:CreditCard
2022/12/15 West End Dentistry
Expenses:Dental $160.00 ; two fillings
Liabilities:CreditCard
We want to categorize three of the above transactions (costume, doctor visit after overeating candy, and fillings as a result of too consuming too many sugary Halloween treats) as expenses attributable to Halloween. One option would be to create a new category of expenses for Halloween, but what if we want to also keep the other categories, such as Entertainment, Medical, and Dental? For example, we want to have the visit to the dentist categorized as a dental expense, but at the same time we should be able to find it under expenses related to Halloween. One solution is to add a tag for Halloween.
Mechanics of Writing a Tag
One of the simplest tags is a word, which is inside a comment, immediately followed by a colon. In the file below, you will see that three of the transactions have a tag, named Halloween, added to them:
2022/09/25 ACME Costume ; Halloween:
Expenses:Entertainment $45.99 ; Amoeba Man
Liabilities:CreditCard
2022/10/30 Cheap-O-Gas
Expenses:Automotive $37.15
Liabilities:CreditCard
2022/11/01 Medical Associates ; Halloween:
Expenses:Medical $80.00 ; upset stomach
Liabilities:CreditCard
2022/12/15 West End Dentistry ; Halloween:
Expenses:Dental $160.00 ; two fillings
Liabilities:CreditCard
Using Tags
Once we incorporate tags in our hledger file, we can use them in a variety of ways. Here, we will look at one way, which is to use the tags to filter a command.
This command:
$ hledger register Expenses tag:Halloween
Produces something like the following output (i.e., all the Expenses tagged “Halloween”):
2016/09/25 ACME Costume Liabilities:CreditCard -$45.99 -$45.99
2016/11/01 Medical Associates Liabilities:CreditCard -$80.00 -$125.99
2016/12/15 West End Dentistry Liabilities:CreditCard -$160.00 -$285.99
Notice that the automotive expense for gasoline does not appear in the above output. This is because the transaction was not tagged as “Halloween.” Thus, you see how you can narrow the scope of a command such as register, so that the command applies to just those transactions with a specific tag.
And because we chose to use a tag, as opposed to creating a new expense category, we can still find out what we spent on dentistry, doctor’s bills, and so on.
Adding Values to Tags
Just being able to create a simple tag is frighteningly useful. However, you can also add values to tags, so that you can narrow a search to a tag value. This is a real treat to learn how to do!
For this example, we will be using expenses of the mummy, a monster popular in fiction and film. Our mummy in question likes to have a variety of changes of wrappings, and so it has made several purchases from its favorite online store:
2022/05/13 AcmeWrappings.com
Expenses:Clothing $28.00
Liabilities:MonsterCard
2022/05/15 AcmeWrappings.com
Expenses:Clothing $44.90
Liabilities:MonsterCard
2022/05/17 AcmeWrappings.com
Expenses:Clothing $36.50
Liabilities:MonsterCard
2022/05/20 AcmeWrappings.com
Expenses:Clothing $58.99
Liabilities:MonsterCard
2022/05/28 AcmeWrappings.com
Expenses:Clothing $39.00
Liabilities:MonsterCard
The Mechanics
The mummy would like to note the color of each wrapping it purchases. Therefore, it creates a tag “color” and adds one or more words telling the color. For example, the transaction below has the tag “color” and the value is “ancient white”:
2022/05/15 AcmeWrappings.com
Expenses:Clothing $44.90 ; color:ancient white
Liabilities:MonsterCard
Note that you can optionally put a space after the colon, as shown immediately below:
2022/05/15 AcmeWrappings.com
Expenses:Clothing $44.90 ; color: ancient white
Liabilities:MonsterCard
Here is the file that we will be working with:
2022/05/13 AcmeWrappings.com
Expenses:Clothing $28.00 ; color:parchment
Liabilities:MonsterCard
2022/05/15 AcmeWrappings.com
Expenses:Clothing $44.90 ; color:ancient white
Liabilities:MonsterCard
2022/05/17 AcmeWrappings.com
Expenses:Clothing $36.50 ; color:dust
Liabilities:MonsterCard
2022/05/20 AcmeWrappings.com
Expenses:Clothing $58.99 ; color:ancient white
Liabilities:MonsterCard
2022/05/28 AcmeWrappings.com
Expenses:Clothing $39.00 ; color:parchment
Liabilities:MonsterCard
In the above file, we have added values for the color of each of the wrappings that were purchased, specifically,
dust
parchment
ancient white
Let’s say we want to list all the wrappings that were dust colored.
We could use the command:
$ hledger register tag:color=dust
The above would output something like the following (i.e., the one expense that was tagged with the color dust:
2022-05-17 AcmeWrappings.com Expenses:Clothing $36.50 $36.50
On the other hand, if we wanted to list only the wrappings that were parchment color, we could use the command:
$ hledger register tag:color=parchment
The above command, lists the following:
2022-05-13 AcmeWrappings.com Expenses:Clothing $28.00 $28.00
2022-05-28 AcmeWrappings.com Expenses:Clothing $39.00 $67.00
Watch Out for the Spaces
Now, let’s look for every wrapping colored “ancient white”. If we try:
$ hledger register tag:color=ancient white
We get exactly nothing. What happened? As you most likely guessed from the title of this section, the space between “ancient” and “white” is the cause of the problem. To fix this, we can put the text in quotation marks.
Therefore, you will need something like the following, to find all the wrappings colored “ancient white”:
$ hledger register tag:color="ancient white"
Combining Tags and Comments
What if you want both a tag and a comment in the same line? A tag goes in a comment, but what are the restrictions? Glad you asked that question!
Answer: hledger tags go inside a comment, and there are two options. First, the tag can go at the end of the comment.
For example, you make a purchase of an inflatable pumpkin and want to include that fact in a comment. However, in the same line you also want to tag it as holiday:Halloween. One option is to write a comment followed by a tag:
2022/09/26 ACME Holiday Supplies
Expenses:Entertainment $58.99 ; inflatable pumpkin holiday:Halloween
Liabilities:CreditCard
The second option is to end the tag with a comma. You can then put a comment after the comma.
For example, we have the tag first (holiday:Halloween) followed by a comma. After the comma is a comment (inflatable pumpkin).
2022/09/26 ACME Holiday Supplies
Expenses:Entertainment $58.99 ; holiday:Halloween, inflatable pumpkin
Liabilities:CreditCard
Note that if you put a comment after the tag without separating the tag and comment with a comma, the comment becomes part of the tag value, and this can cause unwanted results. You do not want unwanted results when working with important data!
Finally, it’s possible to have a comment, followed by a tag, and as long as you end the tag in a comma, you can have additional comments after the comma at the end of your tag.
Multiple Tags
Our friend the mummy, introduced earlier, is very happy being able to track the color of its wrappings. In addition, however, it would also like to track the type of fabric and the width of the cloth. How does it do this?
Fortunately, hledger allows multiple tags, with each tag separated by a comma. Here is an example:
; fabric:cotton, width:15, color:parchment
In addition to recording the above, our mummy wants to track which type of wrappings it buys. Specifically, it needs to track the type of fabric, the width of the cloth, and the color of the material. How does it do this?
Fortunately, hledger allows multiple tags, with each tag separated by a comma. Here is an example:
; fabric:cotton, width:15, color:parchment
The mummy now adds the tags to each purchase, resulting in:
2022/05/13 AcmeWrappings.com
Expenses:Clothing $28.00 ; fabric:cotton, width:15, color:parchment
Liabilities:MonsterCard
2022/05/15 AcmeWrappings.com
Expenses:Clothing $44.90 ; fabric:nylon, width:20, color:ancient white
Liabilities:MonsterCard
2022/05/17 AcmeWrappings.com
Expenses:Clothing $36.50 ; fabric:wool, width:15, color:dust
Liabilities:MonsterCard
2022/05/20 AcmeWrappings.com
Expenses:Clothing $58.99 ; fabric:wool, width:20, color:ancient white
Liabilities:MonsterCard
2022/05/28 AcmeWrappings.com
Expenses:Clothing $39.00 ; fabric:cotton, width:30, color:parchment
Liabilities:MonsterCard
To see everything purchased made of wool, the mummy types:
$ hledger register tag:fabric=wool
The result is:
2022/05/17 AcmeWrappings.com Expenses:Clothing $36.50 $36.50
2022/05/20 AcmeWrappings.com Expenses:Clothing $58.99 $95.49
Or to see which wrappings had a width of 20:
$ hledger register tag:width=20
which outputs…
2022/05/15 AcmeWrappings.com Expenses:Clothing $44.90 $44.90
2022/05/20 AcmeWrappings.com Expenses:Clothing $58.99 $103.89
Multiple Tags in a Query
Multiple tags can be used in the same query. For example, our monster friend wants to find out how much parchment colored cotton it has purchased:
$ hledger register tag:fabric=cotton tag:color=parchment
Notice that there is no comma between the two tags in the above query. You use the comma to separate tags in the data file, but not the query.
By the way, the above command outputs:
2022/05/13 AcmeWrappings.com Expenses:Clothing $28.00 $28.00
2022/05/28 AcmeWrappings.com Expenses:Clothing $39.00 $67.00
Each wrapping purchased above is both made of cotton AND has the color of parchment.
If you wish to find all wrappings, say, made of wool OR colored parchment, you can run two queries. First, we find all the wool wrappings:
$ hledger register tag:fabric=wool
Then we find all the parchment colored ones:
$ hledger register tag:color=parchment
Expenses per Category
One common question is how much did we spend in total for each tag value? For example, how much did the mummy spend on each color of wrapping? To find out, we can run a balance report for our expenses pivoted by color:
$ hledger balance expenses --pivot color
The result is:
$103.89 ancient white
$36.50 dust
$67.00 parchment
--------------------
$207.39
Aha, we see a lot of spending on “ancient white”.
Similarly, if we want to see the expenses by type of fabric, we type:
$ hledger balance expenses --pivot fabric
And we get:
$67.00 cotton
$44.90 nylon
$95.49 wool
--------------------
$207.39
From the above, we observe that the mummy has spent the more on wool wrappings than on either cotton or nylon ones.
Summary
This tutorial has shown how to:
-
Add a tag to an hledger transaction -
Use tags with the register command to list only those expenses with a given tag -
Add values to tags -
Use tag values with the register command to list only those expenses a given tag with a given value -
Combine comments and tags in the same line -
Use multiple tags in the same line -
Use the --pivot option to total expenses by tag value
Hledger Tag Summary with Examples
Add a tag to an hledger transaction
Inside a comment write a word immediately followed by a colon:
2022/09/25 ACME Costume ; Halloween:
Expenses:Entertainment $45.99
Liabilities:CreditCard
A tag can apply to a whole transaction, as in the above, or just one of the lines as in:
2022/09/25 ACME Costume
Expenses:Entertainment $45.99 ; Halloween:
Liabilities:CreditCard
Use tags with the register command to list only the expenses for a given tag
$ hledger -f Halloween2.hledger register Expenses tag:Halloween
Add values to tags
To add a value to a tag, add one or more words to the tag:
2022/05/20 AcmeWrappings.com
Expenses:Clothing $58.99 ; color:ancient white
Liabilities:MonsterCard
Limit a command to tags with specified values
$ hledger register tag:color="ancient white"
Note that if your tag value has a space in it, you must do something such as put quotation marks around the entire tag value. If there are no spaces in your tag value, the quotation marks are optional.
Combine comments and tags
Two options. The first is to write comments first, and put tags at the end:
2022/10/31 Grocery Store
Expenses $3.52 ; on sale today item:candy
Liabilities:CreditCard
The second option is to put a comma after the tag, and then add a comment after the comma:
2022/10/31 Grocery Store
Expenses $3.52 ; item:candy, on sale today
Liabilities:CreditCard
You can even have comment, tag, comma, and comment.
Use multiple tags on the same line
Separate multiple tags with a comma:
2022/05/20 AcmeWrappings.com
Expenses:Clothing $58.99 ; fabric:wool, width:20, color:ancient white
Liabilities:MonsterCard
Use the –pivot option to total expenses by tag value
$ hledger balance expenses --pivot color
or
$ hledger balance expenses --pivot fabric
Conclusion
Tag, you’re it.
Time planning
Some notes relating to time tracking and budgeting.
See also
- hledger manual > Timedot format
- hledger manual > Timeclock format
- https://github.com/linuxcaffe/task-timelog-hook - use hledger for time reporting with Taskwarrior’s task start/stop times.
A time budgeting workflow
A summary of Simon’s 2018-2019 time budgeting workflow:
I keep a hledger timedot file open in a text-mode Emacs in a drop-down iTerm hotkey window.
Each 15-minute chunk is logged with a dot.
I group the dots into hours for quick visual scanning.
I use comments as temporary reminders, if needed.
Sometimes I use M-x align-regexp, [.;] to line up the dots (or comments)
2019-01-08
fos.hledger.sup .
adm.email ..
adm.finance .... .... ..
fos.hledger.issues.941 .... .
fos.plaintextaccounting ; 1045
biz.dev ; do such and such
I’ve trained myself to update this often while at the computer, and before walking away. If I forget, retroactive logging is also pretty easy. These help:
- work in quarter/half/whole hour chunks, preferably synced with the clock
- have a script or updating terminal pane showing today’s sleep/wake/timelog-saved times, to jog the memory.
Not every day is the same; this system has been quick and flexible enough to suit a range of conditions.
How to set up a time budget
I can set daily/weekly/monthly time budgets if I want:
- create a
time.journalwhich includes your (timedot or timeclock) time log file (assuming you’re not tracking time in journal format)# time.journal include time.timedot - choose a budget interval, eg daily, weekly or monthly
- if you have some historical timelog data, review average spending on that interval to get a baseline
$ hledger -f time.journal date:thisyear bal -WA - in time.journal add a periodic transaction rule to allocate budget amounts, similar to baseline, on that interval
~ weekly (adm:time) 1h (ser:some:proj) 4h - run a budget report, using the same interval:
$ hledger -f time.journal bal --budget -W Balance changes in 2017/11/27w48: || 2017/11/27w48 ===============++=================== adm:time || 0.25h [25% of 1h] ser:some:proj || 0.75h [19% of 4h] ---------------++------------------- || 1.00h
Simon’s hledger time dashboard (2018)
Here’s how I have been logging time for a few years. I have four files:
-
time-2018.timedot is the current year’s time log. It contains daily entries in timedot-ish format, like:
2018/5/7 adm.email 20m inc.client1.enh.1342 .... . inc.client1.enh.1188 . inc.client1.enh.1335 .. fos.hledger.support -
time.journal is in journal format so it can include multiple timedot file(s) and provide an account alias allowing period instead of colon in account names:
; allow . as subaccount separator in timedot files alias /\./=: ;include time-2016.timedot ;include time-2017.timedot include time-2018.timedot -
time-daily.budget defines some daily goals, optionally date-bounded, using periodic transaction(s):
~ daily ; [from Y/M/D] [to Y/M/D] (adm) 1 ; your goals here (inc) 1 (fos) 1 -
time-weekly.budget defines some weekly goals. I like to set these independently of the above, which in current hledger means they must be in a separate file:
~ weekly ; [from Y/M/D] [to Y/M/D] (adm) 1 (biz) 1 (inc) 1 (fos) 1
The monthly budget reuses the weekly goals.
I have an iTerm2 Hotkey Window (a terminal that drops down on ALT-space) with six panes:
-
a bash script showing latest laptop wake/sleep and timelog save times, as a memory aid for time logging:
tlogwatch(Here’s that script, useful only to mac users; please show me something better!):
# record timelog update times export TIMELOG=$HOME/time-2018.timedot alias timelogsaved="ls -lT $TIMELOG | cut -d' ' -f8-11" alias timelogaccessed="ls -lTu $TIMELOG | cut -d' ' -f8-11" alias timelogcreated="ls -lTU $TIMELOG | cut -d' ' -f8-11" # show mac sleep/wake & display on/off events alias wakelog="pmset -g log | egrep -E '((Sleep|Wake) +\t|Display is)' | sed -E \ -e 's/(Notification|Sleep|Wake) * //' \ -e 's/is turned //' \ -e 's/Entering Sleep state/Sleep/' \ " # show recent wakeup/timelog save times to help with time logging, clipped to screen width # The width clipping is to help watch display this in dashboard. function tlog() { LINES=${1:-20} ( wakelog | tail -$LINES printf " \n" printf "$(timelogcreated) timelog created\n" printf "$(timelogsaved) timelog saved\n" printf "$(timelogaccessed) timelog accessed\n" ) | cut -c-$(expr $COLUMNS - 1) } # run a brief tlog report periodically, passing any args to watch function tlogwatch() { LINES=${1:-20} shift watch -t -n60 $@ "bash -ic 'tlog '$LINES" } # TODO why does "tlogwatch 10" give "sh: 10: command not found" ? -
a text-mode emacs for updating the time log:
emacs -nw time-2018.timedot -
-5: updating (using entr) time budget reports for the current day/week/month, using hledger 1.9.1+:
ls time.journal time-2018.timedot time-daily.budget | entr sh -c 'clear; hledger -f time.journal -f time-daily.budget bal --budget -1 -D date:today-tomorrow'ls time.journal time-2018.timedot time-weekly.budget | entr sh -c 'clear; hledger -f time.journal -f time-weekly.budget bal --budget -1 -W date:thisweek-nextweek'ls time.journal time-2018.timedot time-weekly.budget | entr sh -c 'clear; hledger -f time.journal -f time-weekly.budget bal --budget -1 -M date:thismonth-nextmonth' -
an updating hledger-ui for exploring time usage (shift-up/down to resize period, shift-left/right to step through time, t to return to today):
hledger-ui --watch --change date:today -f time.journal
Tips for learning hledger
Pace yourself
Starting out with plain text accounting and hledger, learning enough double entry bookkeeping, setting up a new accounting system, and bringing order to your financial life, can be a lot.
Remember these are valuable skills, which take time to master.
Support
Your fellow users can provide Support. The chats are great for that. (Especially matrix chat, which supports persistent history, long messages, code blocks, reactions, voice/video calls, etc.)
Motivation
Find things that get you in the mood and energised.
-
Maybe doing accounts for a friend or family member ?
-
Maybe a video or talk ?
-
Maybe a book ?
Little and often
Accounting is an ongoing activity, best done in regular small doses. The more often you do it, the easier it is, because less has happened and you can remember it. Ten minutes daily can achieve a lot. (Or less, once you get a routine going.)
Small steps
You can start using hledger in very simple ways, and get immediate benefit. If the task feels unclear or overwhelming, identify small steps with clear goals and verifiable results. A good way is to think about your most pressing needs and what kind of report would help.
Example learning plans
For example, you could first build an accurate picture of what you own and owe:
- Take inventory of your debts, loans and assets; write down the names and numbers.
- Record these as “opening balances” transactions (see the quick start docs).
- Make corrections until hledger shows your balances accurately.
Or you could start by tracking movements of money accurately:
- Start recording changes to the cash in your wallet, starting with today’s balance.
- Then start reconciling daily (comparing the reported and actual balance, and troubleshooting any disagreements).
- Then start tracking the balance in your checking account.
- Then start tracking your other bank accounts.
- Then start categorising your incomes and expenses.
- Then find your bank transaction history and manually enter the transactions from the previous week.
Then you could learn how to import transactions from CSV (if you wish):
- Download your checking account transactions as CSV.
- Develop CSV rules so that you can print the CSV as journal entries.
- Learn how to import the new transactions from CSV to your journal.
- Expand this process to your other bank accounts.
- Then try downloading and importing CSV into your journal every day (or every week) for a while.
Look ahead
If you’re starting out with PTA, these are some things you’ll be doing and learning:
- Check your hledger setup, and start a journal file.
- Build up a set of account names/categories best suited to your situation.
- Learn suitable journal entries for your typical transactions.
- Learn how to catch and fix errors easily.
- Learn how to produce reports useful for you.
- Find convenient and efficient workflows for data entry, reconciling, reporting, backups, etc.
Get good at reconciling
Establish the habit of checking that your reported balances agree with the real world. And learn the troubleshooting techniques (such as viewing the detailed transactions in an account). It is surprising how quickly small events can slip through the cracks and cause confusion. Reconciling often will save you time.
Imperfection
Your bookkeeping does not have to be perfect or even completely accurate (though, you may find accurate bookkeeping satisfying and confidence-building!)
As you practice, you will naturally learn more about the tools and about double-entry accounting, such as how to organise your account categories, and how to write effective journal entries for various real-world events (transactions).
Later you can come back and improve your old journal entries if you wish. You can decide what level of accuracy you need.
Fun
Find ways to make it fun. Connecting with the PTA community online can be one way. And take breaks.
Track changes with version control
You don’t need to do this, but it’s a nice way to keep track of changes to your data.
git
Start tracking changes:git init && git add 2017.journal && git commit 2017.journal -m "first commit"
View uncommitted changes: git status, git diff
Commit changes: git commit 2017.journal -m "updates"
View past commits: git log
darcs
darcs init && darcs add 2017.journal && darcs record 2017.journal -m "first commit"
darcs whatsnew, darcs diff
darcs record 2017.journal -m "updates"
darcs log
etc.
Track investments (2017)
You can use hledger to track stock investments. In fact, the double-entry accounting is flexible enough to support most constellations you will come across. However, you may find that some transactions could be better supported. Caveats are:
- hledger does not validate the cost basis during a sale.
- historical mark-to-market performance is not supported (but the market value at one instant, like today, can be calculated)
Example
Buying a stock
Let’s go over a simple example using costs:
2017/1/1 opening balance
(assets:depot) $3000
2017/1/2 buy shares at $200
; let's assume no fees
assets:shares 10 TSLA @ $200 ; transaction/purchase price
assets:depot
Some reports. We start with $3000. After the 1/2 purchase, we have $1000 remaining and 10 TSLA shares:
$ hledger -f t.j bal --flat -HD
Ending balances (historical) in 2017/01/01-2017/01/02:
|| 2017/01/01 2017/01/02
===============++============================
assets:depot || $3000 $1000
assets:shares || 0 10 TSLA
---------------++----------------------------
|| $3000 $1000, 10 TSLA
Show the shares’ value at cost, with -B/--cost:
$ hledger -f t.j bal --flat -HD -B
Ending balances (historical) in 2017/01/01-2017/01/02:
|| 2017/01/01 2017/01/02
===============++=========================
assets:depot || $3000 $1000
assets:shares || 0 $2000
---------------++-------------------------
|| $3000 $3000
Value reporting
Add the following to the journal file.
; market price, has jumped since yesterday's purchase!
P 2017/1/3 TSLA $250
Show the shares’s value using the latest applicable market price,
with -V/--value.
A $500 capital gain is apparent in the totals:
$ hledger -f t.j bal --flat -HD -V
Ending balances (historical) in 2017/01/01-2017/01/02:
|| 2017/01/01 2017/01/02
===============++=========================
assets:depot || $3000 $1000
assets:shares || 0 $2500
---------------++-------------------------
|| $3000 $3500
There are still limitations in the value reporting that hledger can currently do. More information can be found in Github issue #131 and Github issue #329.
You may want to investigate the output after adding more prices to the journal file.
P 2017/1/1 TSLA $210
P 2017/1/4 TSLA $250
P 2017/1/8 TSLA $270
Selling a stock and tracking capital gains
At some point you will probably sell shares. It may seem intuitive to model such a sale as follows.
2017/1/4 sell shares at $250 ; NOTE: You probably want to model capital gains too; see below
assets:shares -10 TSLA @ $250 ; sell price
assets:depot
This leads to the following evolution
hledger -f t.j balance --flat -HD
Ending balances (historical) in 2017/01/01-2017/01/04:
|| 2017/01/01 2017/01/02 2017/01/03 2017/01/04
===============++=======================================================
assets:depot || $3000 $1000 $1000 $3500
assets:shares || 0 10 TSLA 10 TSLA 0
---------------++-------------------------------------------------------
|| $3000 $1000, 10 TSLA $1000, 10 TSLA $3500
You end up with the correct amount in your depot. At some point, however, you will have to report the capital gain that you realized with your sale. This gain is currently invisible. In fact, we have violated the double-entry principle and created money out of nowhere.
Let’s report our sale in a different way.
2017/1/4 sell shares at $250
assets:shares -10 TSLA @ $200 ; cost basis (must be tracked by user!)
assets:depot $2500 ; cash proceeds
revenue:capital_gains ; deduce profit
Now, the new $500 are correctly balanced with the capital gains account.
hledger -f t.j balance --flat -HD
Ending balances (historical) in 2017/01/01-2017/01/04:
|| 2017/01/01 2017/01/02 2017/01/03 2017/01/04
=======================++=======================================================
assets:depot || $3000 $1000 $1000 $3500
assets:shares || 0 10 TSLA 10 TSLA 0
revenue:capital_gains || 0 0 0 $-500
-----------------------++-------------------------------------------------------
|| $3000 $1000, 10 TSLA $1000, 10 TSLA $3000
Further reading
- Beancount guides (general double-entry accounting advice from another tool)
- Cookbook: Account naming, basic trading transactions
- Trading guide: More complicated trading transactions, discussion on tricky cost basis adjustments
- Github #624 on investment tracking
- Discussion on investment modeling from the mailing list
Track investments (2020)
Here’s a tutorial on tracking “investments” - stocks, cryptocurrencies, and similar - in hledger. This is a more in-depth version of Tracking investments, using hledger 1.17; older hledger versions may not match this doc. I hope to teach you a little basic investment accounting, or a little about doing it with hledger (and other PTA tools), or a little of both. Notably, you’ll see three different ways to record lots and capital gains:
-
using {} notation (works in Ledger, Beancount)
-
using just @ notation (works in hledger, Ledger, Beancount..)
-
using just standard postings (works in any double-entry accounting system).
A stock purchase
We’ll use a cryptocurrency and fictitious prices here, but these examples apply equally well to stocks. Let’s say we start the year with $1000 for investment:
2020-01-01 opening balances
assets:bank:checking $1000
equity:opening/closing balances
In february we see ADA is priced at $0.02, and decide to buy a little. We’ll spend $40, which at 2 cents per ADA buys 2000 ADA. Here’s the most straightforward journal entry:
2020-02-01 buy ada
assets:cc:ada 2000 ADA
assets:bank:checking -$40
We paid $40 (from one asset) and received 2000 ADA in exchange (another asset).
The exact account names don’t matter; here we’ve used a cc subaccount to group all cryptocurrencies,
and an additional ada subaccount to distinguish ADA holdings from other cryptocurrencies
(this can be helpful in some reports.)
In real life there’ll probably be another posting or two, for transaction expenses. We’ll omit those for now.
There are a few more things to say about this entry…
How does that balance ?
As you may know, in hledger and other PTA tools the primary rule is:
- “Each transaction must balance - its amounts must add up to zero”.
Well, in hledger (and Ledger), there’s another rule:
- “When a transaction’s unbalanced amounts involve exactly two commodities, assume one is converted to the other with a conversion rate that makes the transaction balance.”
We can use hledger print -x (x for explicit) to show the above with
all amounts and the inferred conversion price:
$ hledger print -x
2020-02-01
assets:cc:ada 2000 ADA @@ $40
assets:bank:checking $-40
This @ notation has two forms: @@ TOTALPRICE or @ UNITPRICE. In
hledger we call it “cost” (generically for both purchases and sales);
it records the price/conversion rate that was used in this specific transaction.
Note this more explicit journal entry has some redundancy, but that’s no harm - it makes things clearer to the human reader, it provides an extra test of the tool’s rounding and precision, and it helps guard against typos. So we’ll use this one.
But here are two more ways of writing the above, just for completeness:
2020-02-01
assets:cc:ada 2000 ADA @@ $40
assets:bank:checking ; $-40 inferred here
2020-02-01
assets:cc:ada 2000 ADA @ $0.02
assets:bank:checking ; -(2000 x $0.02) = $-40 inferred here
A more correct entry
Do you find this still wrong ? Some amount of dollars seems to disappear, and some amount of ADA appears. Doesn’t this violate the accounting equation, the foundation of double entry bookkeeping, which essentially says “money is not created out of thin air” ? #1177 agrees with you; in summary, it seems the correct entry for this transaction is more like:
2020-02-01
assets:cc:ada 2000 ADA
equity:conversion -2000 ADA
equity:conversion $40
assets:bank:checking -$40
This entry might look a little strange to you, but it does show more clearly that the transaction is balanced; it gathers commodity conversions into a single “conversion” account which can provide useful information; and it preserves the accounting equation. The “conversion” account name isn’t special, but it’s an equity account. Another way to write it, relying on automatic transaction balancing, is:
2020-02-01
assets:cc:ada 2000 ADA
assets:bank:checking -$40
equity:conversion ; -2000 ADA, +$40 inferred here
There’s a problem, however: hledger (and other PTA tools) do not
recognise these more general forms as a commodity conversion, so will
not be able to show cost reports (with -B, at least..).
For this reason,
and considering that the unbalanced accounting equation often does not affect your everyday reports,
you might prefer to stick with the @ notation.
Or, use both equity postings and @ notation together, which might give the best of both worlds.
What about lots ?
If you know a little about investing, or have read Ledger or Beancount docs, you’ll know that it’s important to track the date and cost of each stock purchase, or lot. This information is needed:
-
While holding investments, to calculate unrealised capital gains/losses, ie the change in their value “on paper” as market prices fluctuate.
-
When selling an investment, so that:
-
We can select the right lots to sell. Tax law may require, eg, selling the oldest lots first (the FIFO strategy - First In, First Out).
-
We can calculate the realised capital gain/loss from this sale, by comparing the selling price with the original cost (AKA cost basis) of those lots.
-
Ledger and Beancount provide a special syntax and some builtin reports for tracking lots and calculating capital gains. Currently, hledger does not (aside from a little support for lot syntax). So for now, how can we track lots in hledger ?
We can use the obvious categorisation feature: accounts. We’ll give every lot its own uniquely-named subaccount. Since we already have an account just for ADA, we’ll just name the lot subaccounts by the lots’ purchase dates. If doing multiple ADA purchases per day, we could add a sequence number. We could also include the cost in the name, if we want extra clarity.
So, let’s amend the above journal entry, adding the 20200201 subaccount to represent this first lot:
2020-02-01
assets:cc:ada:20200201 2000 ADA @@ $40
assets:bank:checking $-40
Review
So far, what have we got ? The journal is :
2020-01-01 opening balances
assets:bank:checking $1000
equity:opening/closing balances
2020-02-01
assets:cc:ada:20200201 2000 ADA @@ $40
assets:bank:checking $-40
The balance sheet (truncated for brevity) shows assets are $960 and 2000 ADA:
$ hledger bs --flat | head -10
Balance Sheet 2020-02-01
|| 2020-02-01
========================++================
Assets ||
------------------------++----------------
assets:bank:checking || $960
assets:cc:ada:20200201 || 2000 ADA
------------------------++----------------
|| $960, 2000 ADA
(| head -10 is used here just to hide the empty Liabilities section.
You can omit it if it doesn’t work on your system.)
And with -B/--cost (B for cost basis) we see costs so far are:
$ hledger bs --flat -B |head -10
Balance Sheet 2020-02-01, valued at cost
|| 2020-02-01
========================++============
Assets ||
------------------------++------------
assets:bank:checking || $960
assets:cc:ada:20200201 || $40
------------------------++------------
|| $1000
Market prices
We’ll also start recording the prevailing market price that was in effect at each point in time. This will allow us to report on the market value of our investments.
We don’t need to know it every day or every hour; for this example, we’ll just record it at the start of each month. hledger will assume that market price through the month, until the next one is declared.
Here’s the P (market Price) directive declaring that the market price of ADA, in dollars, on 2020-02-01, was $0.02:
P 2020-02-01 ADA $0.02
And let’s say that ADA’s price has doubled by march 1st; we’ll record that too:
P 2020-03-01 ADA $0.04
We’ll add these to the main journal for simplicity.
Or if you prefer you can keep them in a separate file, eg
2020.prices, adding include 2020.prices to the main journal.
Note that the initial $0.02 market price, and the $0.02 transaction price we recorded above, are the same in this example. In real life they may not be exactly the same, but normally they will be quite similar.
Value reports; some gotchas
Now we have enough data to do a little value reporting. This can be a little confusing at first, so here are a few examples.
Our journal so far is:
2020-01-01 opening balances
assets:bank:checking $1000
equity:opening/closing balances
2020-02-01
assets:cc:ada:20200201 2000 ADA @@ $40
assets:bank:checking $-40
P 2020-02-01 ADA $0.02
P 2020-03-01 ADA $0.04
Let’s check the current market value (AKA mark to market) of our holdings.
-V is a simple form of the --value flag:
$ hledger bs --flat -V |head -10
Balance Sheet 2020-02-01, current value
|| 2020-02-01
========================++============
Assets ||
------------------------++------------
assets:bank:checking || $960.00
assets:cc:ada:20200201 || $80.00
------------------------++------------
|| $1040.00
But the ADA market value looks wrong - on 2020-02-01 it was $40, not $80. So it’s wise to check the manual. In particular, note: “For single period reports, the valuation date is today (equivalent to –value=now)”. So even though the report’s end date is 2020-02-01 (the date of the last transaction), hledger picked “now” as the valuation date, and therefore used our latest 2020-03-01 P directive.
We can fix this by specifying an explicit report end date,
which also sets the valuation date.
We’ll use -e to specify 2020-02-01:
$ hledger bs --flat -V -e 2020-02-01 |head -10
Balance Sheet 2020-01-31, current value
|| 2020-01-31
======================++============
Assets ||
----------------------++------------
assets:bank:checking || $1000.00
----------------------++------------
|| $1000.00
======================++============
Now there’s no ADA balance at all - what gives ? Remember that end dates are exclusive, so the ADA purchase on 2020-02-01 is excluded. With a later end date, eg 2020-02-02, we see it:
$ hledger bs --flat -V -e 2020-02-02 |head -10
Balance Sheet 2020-02-01, current value
|| 2020-02-01
========================++============
Assets ||
------------------------++------------
assets:bank:checking || $960.00
assets:cc:ada:20200201 || $40.00
------------------------++------------
|| $1000.00
And if we specify an end date after the second price directive, we’ll see the value at that date:
$ hledger bs --flat -V -e 2020-03-02 |head -10
Balance Sheet 2020-03-01, current value
|| 2020-03-01
========================++============
Assets ||
------------------------++------------
assets:bank:checking || $960.00
assets:cc:ada:20200201 || $80.00
------------------------++------------
|| $1040.00
Often, a multiperiod, eg monthly, report makes things clearer.
For this case the manual says:
“valuation date … for multiperiod reports, it is the last day of each subperiod”.
For this example we still need to specify the end date though,
otherwise the report will stop at the end of the month containing
the last transaction (ie, 2020-02-29).
This time we’ll say -e apr (or -e 202004), which is less typing
and includes all of march:
$ hledger bs --flat -M -V -e apr | head -10
Balance Sheet 2020-01-31,,2020-03-31, valued at period ends
|| 2020-01-31 2020-02-29 2020-03-31
========================++====================================
Assets ||
------------------------++------------------------------------
assets:bank:checking || $1000.00 $960.00 $960.00
assets:cc:ada:20200201 || 0 $40.00 $80.00
------------------------++------------------------------------
|| $1000.00 $1000.00 $1040.00
Finally, we can clearly see the value of our holdings over time. No ADA, just dollars, in january; ADA worth $40 when purchased, in february; and worth $80, thanks to the increase in market price, in march.
Just for comparison, here’s the same report but showing cost instead of value. Of course cost is not affected by market prices:
$ hledger bs --flat -M -B -e apr | head -10
Balance Sheet 2020-01-31,,2020-03-31, valued at cost
|| 2020-01-31 2020-02-29 2020-03-31
========================++====================================
Assets ||
------------------------++------------------------------------
assets:bank:checking || $1000.00 $960.00 $960.00
assets:cc:ada:20200201 || 0 $40.00 $40.00
------------------------++------------------------------------
|| $1000.00 $1000.00 $1000.00
Unrealised capital gain
The difference between the $40 purchase cost of the ADA, and its $80 value in march, is an unrealised capital gain. “Unrealised” (and therefore not yet taxable, typically) because we haven’t yet sold the ADA and captured the gain in our base currency.
A sale
The next day, we decide to sell all the ADA, just to test the process and capture a little profit. Assuming that this will look much like the purchase transaction in reverse, using the @ notation again, we come up with this:
2020-03-02 sell all ada
assets:cc:ada:20200201 -2000 ADA @ $0.04
assets:bank:checking $80
For a little extra error checking, this time we used the @ UNITPRICE
form, so we can visually check that the per-unit cost looks correct
(at or close to the market price).
Here’s the new balance sheet, with -E (empty) to make it show the
now empty ada account:
$ hledger bs --flat -e apr -E | head -10
Balance Sheet 2020-03-31
|| 2020-03-31
========================++============
Assets ||
------------------------++------------
assets:bank:checking || $1040.00
assets:cc:ada:20200201 || 0
------------------------++------------
|| $1040.00
Realised capital gain
Our dollar balance has increased, from $1000 to $1040, but somewhat magically - there seems to be no transaction causing it. This seems like a bad sign. And indeed a full balance sheet including equity shows a non-zero total, confirming that the Accounting Equation has been disturbed:
$ hledger bse --flat
Balance Sheet With Equity 2020-03-02
|| 2020-03-02
=================================++============
Assets ||
---------------------------------++------------
assets:bank:checking || $1040.00
---------------------------------++------------
|| $1040.00
=================================++============
Liabilities ||
---------------------------------++------------
---------------------------------++------------
||
=================================++============
Equity ||
---------------------------------++------------
equity:opening/closing balances || $1000.00
---------------------------------++------------
|| $1000.00
=================================++============
Net: || $40.00
$40 has appeared from somewhere. This increase is the realised capital gain, which is considered a revenue. If we had sold at a lower price than we paid, this number would be negative, representing a capital loss, which is an expense.
We want this gain/loss to be recorded in the journal, to satisfy the accounting equation and keep accurate records, and also because it is typically a taxable event; we’ll need to know all of these revenues/expenses when filing taxes.
Recording capital gain
The sale transaction above is balanced, with no room for an extra revenue posting. If we try, hledger complains:
2020-03-02 sell all ada
assets:cc:ada:20200201 -2000 ADA @ $0.04 = 0 ADA
assets:bank:checking $80
revenues:capital gain $-40
$ hledger print
...
could not balance this transaction (real postings are off by $-40.00)
...
Ledger (and Beancount) will accept an entry like this, if you add a
special {} notation identifying the lot’s original cost. Below, note
the extra {$0.02}, which says “this is a lot, and was purchased at
$0.02 each”. Ledger will calculate the expected capital gain of $40
and will consider this transaction to be balanced:
2020-03-02
assets:cc:ada:20200201 -2000 ADA {$0.02} @ $0.04 = 0 ADA
assets:bank:checking $80
revenues:capital gain $-40
But hledger doesn’t know about lots or capital gains, as mentioned. (hledger 1.17.99+ will parse the {} notation, but ignores it.) So how can we model a stock sale in hledger ? In general, we should:
- convert back to cash using the lot’s cost price
- manually calculate the capital gain (difference of cost and selling price) and record it as a revenue/expense
If we used the @ notation for the purchase, we should use it here too. The sale looks like this:
2020-03-02 sell ada
assets:cc:ada:20200201 -2000 ADA @ $0.02 ; the original cost
revenues:capital gain $-40 ; the capital gain, 2000 x ($0.04-$0.02)
assets:bank:checking $80
Or if we used the more correct entry for the purchase, ie just standard double entry bookkeeping postings, the sale looks like this:
2020-03-02 sell ada
assets:cc:ada:20200201 -2000 ADA
equity:conversion 2000 ADA
equity:conversion $-40 ; the original cost
revenues:capital gain $-40 ; the capital gain
assets:bank:checking $80
If we check the bse report now, we’ll still see a $40 total, but
this is expected because a revenue has been recorded and not yet
merged into equity by “closing the books”. If we were to do that
temporarily:
2020-03-02 close the books, just for testing
revenues:capital gain $40 = $0
equity:retained earnings -$40
We would see the proper zero total:
$ hledger bse --flat
Balance Sheet With Equity 2020-03-02
|| 2020-03-02
=================================++============
Assets ||
---------------------------------++------------
assets:bank:checking || $1040.00
---------------------------------++------------
|| $1040.00
=================================++============
Liabilities ||
---------------------------------++------------
---------------------------------++------------
||
=================================++============
Equity ||
---------------------------------++------------
equity:opening/closing balances || $1000.00
equity:retained earnings || $40.00
---------------------------------++------------
|| $1040.00
=================================++============
Net: || 0
A variation:
some discussions on the web
suggest transferring directly from equity, bypassing revenues. In that
case we would need to remember to include it in our tax reports, eg we
would need to look at hledger bal revenue expenses 'capital gain'
instead of just hledger is.
Related
- Trading With Ledger-cli And Lots and lotter and gader
- Beancount: Command-line Accounting Cookbook: Account naming, basic trading transactions Beancount: Trading with Beancount: More complicated trading transactions, discussion on tricky cost basis adjustments
- Issue #624 Tracking investments in hledger
- Discussion on investment modeling from the mailing list
Inventory accounting
Fundraiser
This example demonstrates accounting for a fundraiser where members sold meat-sticks and chocolate bars. The local organization would check out inventory, sell it, and then pay the regional organization back for the fundraising goods they sold.
In this particular example chocolate bars are sold for $2 and meat-sticks are sold for $1. The local organization gets to keep 30% of the proceeds.
The role of the first three accounts are fairly straight-forward.
assets:cash tracks money in the cash box. assets:inventory tracks
the unsold meat and chocolate stock. liabilities:supplier tracks
the money owed to the supplier.
The interesting account is income:fundraiser. This account summarizes
the effect of the fundraiser on the organization. In our example the
initial effect on the organization is that it acquired $61.60 of debt,
24 meat-sticks, and 32 chocolate bars. As the fundraiser progresses, this
account will hold only the $26.40 of income.
In the real use-case, the members of the organization would check out product to sell. Additional transactions can be used to transfer stock from the organization’s inventory accounts to member-specific accounts.
account assets:cash ; cash
account assets:inventory ; product inventory
account liabilities:supplier ; money owed to supplier
account income:fundraiser ; income attributed to the fundraiser
commodity MEAT ; meat-sticks
commodity CHOC ; chocolate bars
commodity USD ; US dollars
; Price of the fundraiser goods at the supplier's price
P 1-1 MEAT 0.70 USD
P 1-1 CHOC 1.40 USD
1-1 * Supplier | Pick up inventory
assets:inventory 24 MEAT
assets:inventory 32 CHOC
liabilities:supplier -61.60 USD
income:fundraiser
1-2 * Alice | Sold some goods
assets:inventory -20 MEAT
assets:inventory -12 CHOC
assets:cash 44 USD
income:fundraiser
1-3 * Bob | Sold more things
assets:inventory -4 MEAT
assets:inventory -20 CHOC
assets:cash 44 USD
income:fundraiser
1-4 * Supplier | Pay balance
liabilities:supplier 61.60 USD
assets:cash -61.60 USD
With a daily balance change and historical balance reports we can see the effect of each transaction. Over time we’ll be converting inventory into profits.
$ hledger bal -D --pretty
Balance changes in 2021-01-01..2021-01-04:
║ 2021-01-01 2021-01-02 2021-01-03 2021-01-04
══════════════════════╬══════════════════════════════════════════════════════════════════════════════════════════════════════
assets:cash ║ 0 44.00 USD 44.00 USD -61.60 USD
assets:inventory ║ 32 CHOC, 24 MEAT -12 CHOC, -20 MEAT -20 CHOC, -4 MEAT 0
income:fundraiser ║ -32 CHOC, -24 MEAT, 61.60 USD 12 CHOC, 20 MEAT, -44.00 USD 20 CHOC, 4 MEAT, -44.00 USD 0
liabilities:supplier ║ -61.60 USD 0 0 61.60 USD
──────────────────────╫──────────────────────────────────────────────────────────────────────────────────────────────────────
║ 0 0 0 0
hledger bal -DH --pretty
Ending balances (historical) in 2021-01-01..2021-01-04:
║ 2021-01-01 2021-01-02 2021-01-03 2021-01-04
══════════════════════╬═════════════════════════════════════════════════════════════════════════════════════
assets:cash ║ 0 44.00 USD 88.00 USD 26.40 USD
assets:inventory ║ 32 CHOC, 24 MEAT 20 CHOC, 4 MEAT 0 0
income:fundraiser ║ -32 CHOC, -24 MEAT, 61.60 USD -20 CHOC, -4 MEAT, 17.60 USD -26.40 USD -26.40 USD
liabilities:supplier ║ -61.60 USD -61.60 USD -61.60 USD 0
──────────────────────╫─────────────────────────────────────────────────────────────────────────────────────
║ 0 0 0 0
To see the same information in terms of the cash-value of the inventory
that we’re holding we can use the -V flag. From this view we can see the
income realized by the fundraiser as it progresses, and the cash value
of our current inventory.
hledger bal -DV --pretty
Balance changes in 2021-01-01..2021-01-04, valued at period ends:
║ 2021-01-01 2021-01-02 2021-01-03 2021-01-04
══════════════════════╬════════════════════════════════════════════════
assets:cash ║ 0 44.00 USD 44.00 USD -61.60 USD
assets:inventory ║ 61.60 USD -30.80 USD -30.80 USD 0
income:fundraiser ║ 0 -13.20 USD -13.20 USD 0
liabilities:supplier ║ -61.60 USD 0 0 61.60 USD
──────────────────────╫────────────────────────────────────────────────
║ 0 0 0 0
$ hledger bal -DHV --pretty
Ending balances (historical) in 2021-01-01..2021-01-04, valued at period ends:
║ 2021-01-01 2021-01-02 2021-01-03 2021-01-04
══════════════════════╬════════════════════════════════════════════════
assets:cash ║ 0 44.00 USD 88.00 USD 26.40 USD
assets:inventory ║ 61.60 USD 30.80 USD 0 0
income:fundraiser ║ 0 -13.20 USD -26.40 USD -26.40 USD
liabilities:supplier ║ -61.60 USD -61.60 USD -61.60 USD 0
──────────────────────╫────────────────────────────────────────────────
║ 0 0 0 0
For our fundraiser the members got credit for their contribution to the fundraiser. By pivoting by payee we can see the gross revenue and inventory sold by each member. The row for the supplier shows the total inventory acquired and cost paid.
hledger bal income:fundraiser --pivot=payee --pretty -Y
Balance changes in 2021:
║ 2021
══════════╬═══════════════════════════════
Alice ║ 12 CHOC, 20 MEAT, -44.00 USD
Bob ║ 20 CHOC, 4 MEAT, -44.00 USD
Supplier ║ -32 CHOC, -24 MEAT, 61.60 USD
──────────╫───────────────────────────────
║ -26.40 USD
To see the net revenue attributed to each member, we can generate the same report valued by the cost of the goods.
hledger bal income:fundraiser --pivot=payee --pretty -YV
Balance changes in 2021, valued at period ends:
║ 2021
═══════╬════════════
Alice ║ -13.20 USD
Bob ║ -13.20 USD
───────╫────────────
║ -26.40 USD
Tutorial: hledger add
Here we’ll walk you through a simple way of using hledger, the command line interface and built-in add command. This works on all platforms with no further setup. We’ll describe every step in detail.
We’ll also introduce some basic hledger concepts. So even if you don’t plan on using the hledger CLI or add command, it might be worth skimming this tutorial.
Install hledger and let’s get started!
Check your installation
Open a terminal or command prompt, and check your hledger version. It should be reasonably up to date. This doc was last tested with:
$ hledger --version
hledger 1.29
Locate your journal file with “hledger stats”
hledger reads financial transactions from a “journal file” (so named because it represents a General Journal).
By default, it looks for the journal file in your home directory, with filename .hledger.journal.
This is good for getting started, but at some point you might want to customise this
by setting the LEDGER_FILE environment variable.
In the following examples, we’ll assume the default file is being used.
Check your journal file using the stats command. You should see something like:
$ hledger stats
The hledger journal file "/home/YOU/.hledger.journal" was not found.
Please create it first, eg with "hledger add" or a text editor.
Or, specify an existing journal file with -f or LEDGER_FILE.
Most hledger commands read this file but can not change it; the add and web commands can also write it.
(If stats reports that the file exists, eg because you previously created it, move it out of the way temporarily for these exercises.)
Record a transaction with “hledger add”
Follow the help and use the add command to record your first transaction, an imaginary purchase at the supermarket. We’ll go through this in detail. Later you’ll learn other ways to enter data.
$ hledger add
Creating hledger journal file "/home/YOU/.hledger.journal".
Adding transactions to journal file /home/YOU/.hledger.journal
Any command line arguments will be used as defaults.
Use tab key to complete, readline keys to edit, enter to accept defaults.
An optional (CODE) may follow transaction dates.
An optional ; COMMENT may follow descriptions or amounts.
If you make a mistake, enter < at any prompt to restart the transaction.
To end a transaction, enter . when prompted.
To quit, enter . at a date prompt or press control-d or control-c.
Date [2023-05-25]:
add prompts for each transaction field. The first is the date.
The value in square brackets is the suggested default (today’s date). Press enter to accept it.
Description: trip to the supermarket
Transactions have an optional description (a single line of text) to help you understand them.
You can describe the transaction here, or put a payee name, or leave it blank.
Type trip to the supermarket and press enter.
Account 1: expenses
Transactions have two or more accounts. Keep it simple; just enter expenses for the first one.
If you’re thinking “expenses sounds more like a category”: it is, but double entry accounting calls those “accounts”, too. A purchase is a transfer of money from an asset account to an expense account. An asset is something you own, like some money in a bank account or in your pocket. Once the money has been “moved” to an expense, you no longer own it, but the increasing balance in the expense account reminds you where it went.
Amount 1: $10
The amount being “moved” to expenses.
You can use any currency or commodity symbol here; for this example we’ll assume 10 US dollars.
Account 2: assets
Next, specify which account the money comes from. Just say assets.
Amount 2 ? [$-10.0]:
Now you’re asked for the amount to “move” to or from the assets account.
As the default, hledger offers the amount required to “balance” the postings entered so far.
The minus sign indicates the money is moving from this account.
(hledger uses the positive and negative sign instead of accounting’s traditional “debit” and “credit” terminology.)
In a balanced transaction, the sum of posted amounts is zero, in other words no money disappears into thin air.
Press enter to accept the default. It has an extra decimal place, but never mind.
Account 3 (or . or enter to finish this transaction): .
Press enter to finish entering this transaction.
2023-05-25 trip to the supermarket
expenses $10
assets $-10
Save this transaction to the journal ? [y]:
You are given a chance to review the transaction just entered. Here you see hledger’s plain text data format for journal entries: a non-indented YYYY-MM-DD date, space, and description, followed by two or more indented posting lines, each containing an account name, two or more spaces, and an amount. (Account names can contain spaces, so at least two spaces are needed to separate them from the amount.) Press enter to save the journal entry.
Saved.
Starting the next transaction (. or ctrl-D/ctrl-C to quit)
Date [2023-05-25]: <CTRL-D>
hledger has saved it to the journal file and is ready for the next entry. Hold down the control key and press d once to exit.
stats should now report that your journal exists and contains one transaction:
$ hledger stats
Main journal file : /home/YOU/.hledger.journal
Included journal files :
Transactions span : 2023-05-25 to 2023-05-26 (1 days)
Last transaction : 2023-05-25 (0 days ago)
Transactions : 1 (1.0 per day)
Transactions last 30 days: 1 (0.0 per day)
Transactions last 7 days : 1 (0.1 per day)
Payees/descriptions : 1
Accounts : 2 (depth 1)
Commodities : 1 ($)
Market prices : 0 ()
Run time (throughput) : 0.13s (8 txns/s)
Show transactions with “hledger print”
The print command shows a tidied-up view of the transaction entries in your journal. Since there’s just one so far, you should see:
$ hledger print
2023-05-25 trip to the supermarket
expenses $10
assets $-10
Examine your journal file
List and print the journal file (on Windows, use dir and type and the file path from hledger stats):
$ ls -l ~/.hledger.journal
-rw-r--r-- 1 YOU YOU 114 May 25 16:55 /home/YOU/.hledger.journal
$ cat ~/.hledger.journal
; journal created 2023-05-25 by hledger
2023-05-25 trip to the supermarket
expenses $10
assets $-10
Edit the journal file
Since the journal file is plain text, you can edit it directly with any text editor. Let’s open the file in a text editor and test the effect of some changes. Eg:
$ emacs ~/.hledger.journal
A convenience: inferred amounts
First, remove one of the amounts, and save the file. Eg:
2023-05-25 trip to the supermarket
expenses $10
assets
You’ll see that hledger print still works (and shows just the one amount).
This is a convenience to make manual data entry easier: if one amount is missing,
hledger infers the amount needed to balance the transaction ($-10 in this case).
To make such inferred amounts explicit, you can use hledger print -x.
Only one amount can be inferred
Now try removing both amounts, and save the file. This is an invalid entry:
2023-05-25 trip to the supermarket
expenses
assets
hledger print now gives an error:
$ hledger print
hledger: Error: /Users/simon/.hledger.journal:3-5:
3 | 2023-05-25 trip to the supermarket
| expenses
| assets
This transaction is unbalanced.
There can't be more than one real posting with no amount.
(Remember to put two or more spaces between account and amount.)
All hledger commands expect the journal to be well-formed, and will report an error and exit otherwise.
Two spaces
Notice the last part of that error message: “Remember to put two or more spaces between account and amount”. Another cause of this error is forgetting to put two spaces before the amount, like this:
2023-05-25 trip to the supermarket
expenses $10 ; <- problem: only one space between expenses and $10
assets
$ hledger print
hledger: Error: /Users/simon/.hledger.journal:3-5:
3 | 2023-05-31 trip to the supermarket
| expenses $10
| assets
This transaction is unbalanced.
There can't be more than one real posting with no amount.
(Remember to put two or more spaces between account and amount.)
Since account names can contain spaces, you must separate them from anything following them (like an amount, or a comment) by two or more spaces. (Otherwise hledger thinks the account name is “expenses $10”.)
Unbalanced transactions
Edit the file to look like this:
2023-05-25 trip to the supermarket
expenses $10
assets $10 ; <- another problem: both amounts are positive
Here, we wrote both posting amounts but got the sign wrong on one of them, so they don’t add up to zero.
hledger should detect this mistake. Verify it by running some command, eg print. You should see:
$ hledger print
hledger: Error: /Users/simon/.hledger.journal:3-5:
3 | 2023-05-31 trip to the supermarket
| expenses $10
| assets $10
This transaction is unbalanced.
The real postings all have the same sign. Consider negating some of them.
Consider adjusting this entry's amounts, or adding missing postings.
That makes sense. (It calls them “real” postings because there are some other kinds of posting you haven’t learned about yet; they aren’t important.)
Correct the mistake by adding the minus sign, or just removing the assets amount entirely, and verify
that print works again:
$ hledger print
2023-05-25 trip to the supermarket
expenses $10
assets $-10
Record a transaction by editing
Edit the file again and manually add a second purchase transaction. It’s often quickest to copy & paste a similar entry, then change it. Make the file look like this:
2023-05-25 trip to the supermarket
expenses $10
assets $-10
2023-05-26 forgot the bread
expenses $5
assets
The blank line between transactions is customary, though not required.
Test your work with print. You should see:
$ hledger print
2023-05-25 trip to the supermarket
expenses $10
assets $-10
2023-05-26 forgot the bread
expenses $5
assets
What’s in a Transaction ?
Here’s a hledger journal entry for a simple transaction, with the parts named:
And here’s a more complicated journal entry:
Show postings and a running total with “hledger register”
The register command shows transactions in a different format. More precisely, it shows postings.
Remember, a posting is an increase or decrease of some account by some amount, and a transaction contains two or more of them.
Run register and compare with the output of print above. You should see (more or less, depending on your terminal width):
$ hledger register
2023-05-25 trip to the super.. expenses $10 $10
assets $-10 0
2023-05-26 forgot the bread expenses $5 $5
assets $-5 0
Each line represents one account posting within a transaction. The transaction’s date and description is displayed only for the first posting in each transaction. Next we see the posted account’s name and the amount posted. The final column is a running total of the posted amounts, starting from zero.
Show a per-account register report
Notice how the running total above keeps resetting to zero. This makes sense (since we know each transaction’s postings add up to zero) but isn’t very useful. The register report is more useful when we restrict it to a subset of postings - say, only the postings within a single account. You can do this by specifying the account name as a command line argument.
Run a register report for the expenses account. You should see something like the below.
(On POSIX platforms, this command uses the terminal width so the output may look slightly different.
You can force it to look like the below by running export COLUMNS=80 first:
$ hledger register expenses
2023-05-25 trip to the super.. expenses $10 $10
2023-05-26 forgot the bread expenses $5 $15
Now it’s clear that your expenses balance - ie, the total amount spent - has increased to $15.
Your assets balance should have dropped accordingly. Check it:
$ hledger register assets
2023-05-25 trip to the super.. assets $-10 $-10
2023-05-26 forgot the bread assets $-5 $-15
Set initial account balances
hledger assumes every account starts with a zero balance,
so in the previous example, we see the withdrawals producing a negative running balance.
Let’s assume assets represents a real-world asset, like your bank checking account, and you want to start tracking it from 2023-05-01 onward, and on that day it contained exactly $500.
To show the real-world account balance, edit your journal file and add this transaction at the top:
2023-05-01 set initial assets balance
assets $500
equity:opening balances
The other account name doesn’t matter too much; equity:opening balances is conventional.
(You could also use an unbalanced transaction for this if you prefer.)
Now the report looks like this, with an accurate running balance on each date (hledger calls this a historical balance):
$ hledger register assets
2023-05-01 set initial asset.. assets $500 $500
2023-05-25 trip to the super.. assets $-10 $490
2023-05-26 forgot the bread assets $-5 $485
Queries
The account name argument above is an example of a query, a search pattern which restricts a report to a subset of the data. In this way you can make reports focussed on just the data you care about.
Note that the argument is a case-insensitive regular expression which matches anywhere inside the account name.
So “e” would match both expenses and assets.
And if you had an account named other assets, “assets” would also match that, so to match only the assets
account you’d need a more precise pattern like “^assets$”.
(In a regular expression ^ means “match at the beginning” and $ means “match at the end”.)
If this doesn’t make sense, read more about regular expressions.
Multiple query arguments are usually combined as follows: queries on the same field are ORed, and queries on different fields are ANDed. See the linked query doc for more details.
Run the following examples and make sure they make sense, consulting the manual as needed.
Show only transactions whose description contains “bread”:
$ hledger print desc:bread
2023-05-26 forgot the bread
expenses $5
assets
Show only postings on or after a certain date to an account whose name contains “exp”:
$ hledger register date:2023-05-26.. exp
2023-05-26 forgot the bread expenses $5 $5
Show accounts and their balances with “hledger balance”
The third of hledger’s three core reporting commands is balance. Use it to list all the accounts posted to, and their balance. You should see account balances agreeing with the final running total in the register reports above:
$ hledger balance
$485 assets
$-500 equity:opening balances
$15 expenses
--------------------
0
The overall total of these balances is also shown. As with other reports, you can use a query expression to select a subset of the data to report on. Eg:
$ hledger balance assets
$485 assets
--------------------
$485
balance shows balance changes by default
Here’s a balance report based only on the postings dated 2023-05-26:
$ hledger balance date:2023-05-26
$-5 assets
$5 expenses
--------------------
0
As you can see from this, balance does not necessarily report real-world account balances;
more precisely it shows the sum of the postings you have selected to report on,
ie the balance change caused by those postings. When you select a subset of postings,
this may be different from the real historical account balance.
Use register to troubleshoot a balance report
If you’re not sure where a balance report number is coming from those, run a register report with the same arguments, and the account name, to see the individual postings in that account. Eg for assets above, we can see that the date: query caused the earlier postings to be excluded:
$ hledger register date:2023-05-26 assets
2023-05-26 forgot the bread assets $-5 $-5
Use -H to show historical end balances
Adding the -H/--historical flag to the balance report above makes it show historical balances,
instead of balance changes.
Historical balances include the effect of postings before the report start date
(which otherwise would be excluded here by date:2023-05-26):
$ hledger balance date:2023-05-26 -H
$485 assets
$-500 equity:opening balances
$15 expenses
--------------------
0
-H works with register reports too (note the running total is now a running historical balance):
$ hledger register date:2023-05-26 assets -H
2023-05-26 forgot the bread assets $-5 $485
-H is useful when you have specified a report start date to limit the report size,
but you still want to see real world balances.
Easier reports with “hledger bs” and “hledger is”
hledger’s bs/balancesheet and is/incomestatement reports are easier alternatives
to the balance command. They correspond to a standard Balance Sheet and an Income Statement
(Profit and Loss report) respectively. They will automatically select the proper accounts
(though you may need to declare account types to enable this),
and bs uses -H by default.
Also, they show normal liability, equity and revenue amounts as positive numbers,
unlike balance which shows those as negative numbers.
Eg:
$ hledger bs
Balance Sheet 2023-05-26
|| 2023-05-26
=============++============
Assets ||
-------------++------------
assets || $485
-------------++------------
|| $485
=============++============
Liabilities ||
-------------++------------
-------------++------------
||
=============++============
Net: || $485
$ hledger is
Income Statement 2023-05-01..2023-05-26
|| 2023-05-01..2023-05-26
==========++========================
Revenues ||
----------++------------------------
----------++------------------------
||
==========++========================
Expenses ||
----------++------------------------
expenses || $15
----------++------------------------
|| $15
==========++========================
Net: || $-15
Review
You have learned:
- a simple plain text notation for recording financial transactions, used by hledger and Ledger
- what is the journal file, where it is, and how to get statistics on it with
hledger stats - how to record new transactions using
hledger add - how to record transactions by editing the journal file
- what the journal entry for a purchase looks like
- how to detect some common errors, by eye or with hledger
- how hledger selects data with queries, and how to select by account, description, or date
- how to list transactions with
hledger print - how to list postings and see an account’s balance over time with
hledger register - how to list accounts and their balance change or real-world balance, with
hledger balance - how to produce standard reports more easily with
hledger bsandhledger is
Tutorial: hledger-ui
hledger-ui is hledger’s TUI (a full-window terminal interface). It’s fast, efficient, and a bit more featureful than the web UI.
Check your installation
Open a terminal or command prompt and check your hledger-ui version. It should be reasonably up to date. This doc was last tested with:
$ hledger-ui --version
hledger-ui 1.17.1.1
If this fails, check Installing for install and setup tips.
Start hledger-ui
Normally, you start hledger-ui by running hledger-ui in a terminal.
Any standard terminal app will do, but not a command prompt that
doesn’t allow cursor positioning (such as an emacs shell buffer).
(In that case you would see garbage output like ^[(B^[(B──────────── tmp.journal account balances...,
and you would have to press q, RETURN to exit.)
For this tutorial, we’ll specify a new temporary journal file, to suit the steps below and avoid disturbing any existing data:
$ echo > tmp.journal
$ hledger-ui -f tmp.journal
A quick tour
With an empty journal, there’s not much to see. But we can see: the
name of the journal file (tmp.journal) and the screen (account balances)
at the top:

And some quick help across the bottom,
telling us that pressing q is a way to quit hledger-ui,
and pressing a is a way to add transactions.
Press a:

This bare-bones data entry UI is hledger’s add command, also introduced in Tutorial: hledger add.
Here, we’ll use it to enter a few transactions,
similar to the 5-minute quick start example.
After each prompt, type the value shown in white and press RETURN.
Note when we get to Amount 2, hledger guesses that this is a two-posting transaction
and as a default value offers the amount that would balance the transaction ($-1234):

In such cases we can press TAB to enter the default value, with an opportunity to edit it before pressing RETURN:

Or just press RETURN to use it.
At this point transaction is balanced, and so we have the option to end the transaction. Press RETURN (enter) to do that:

We get one more opportunity to preview the resulting journal entry.
Press RETURN again to accept the default answer (y):

When we see “Saved.”, we know the entry has been written (appended) to the journal file.
And we get another Date prompt, ready to enter another transaction:

Continue entering the values shown in white (2020-03-15, client payment..).
This time, based on the description, hledger has picked a “similar transaction”,
which will supply defaults for the following prompts. Eg assets:checking for Account 1.
That’s what we want, so press RETURN to accept it:

The next defaults ($1234, equity..) are not what we want,
so ignore them and continue entering the values shown ($2000, income:consulting..):

And as before, press RETURN a few times to finish and save this transaction:

Enter the values for the final transaction (2020-03-20, Sprouts..).
Note, we don’t accept the default for Amount 2,
so the transaction remains unbalanced, and we can continue on,
to enter the third posting:

When entering a value we’ve used before, we can type just the start of
it and press TAB to auto-complete the rest, saving some work:

Complete this transaction, and this time at the Date prompt enter . (period) and RETURN to end data entry:

We have returned to hledger-ui, and now we see the accounts and their ending balances:

Press DOWN (down arrow) to move the selection (the yellow highlight) to the assets:checking account:

Press RIGHT to “drill in” to see the transactions in assets:checking (“register” screen).
The third column shows the other account(s) involved, abbreviated when necessary.
The last column is the account’s running balance:

Press RIGHT again to see the selected transaction’s journal entry (“transaction” screen).
We can see this is transaction number 3 in the journal, and also number 3 of 3 in the assets:checking account:

On this screen we can use the up and down arrows to step back and forth through transactions.
Press UP. Now we’re at transaction number 2:

Press LEFT to go back to the previous screen (assets:checking’s register):

And LEFT again to go back to the top screen (accounts):

Press T (capital T) to switch from “flat mode” to “tree mode”. This shows the account hierarchy more clearly:

With a lot of accounts, sometimes you want to see less detail.
Press the - (minus) key once to reduce the account depth limit by 1.
Now the expenses:food:groceries subaccount (depth 3) has been hidden,
and the heading says “to depth 2”:

Press the - key once more, reducing the depth limit to 1:

We could press + to increase the depth again, or a number key to set it directly.
Press 9 to make it “unlimited”, removing the depth limit:

Press the / (forward slash) key to enter a filter query.
A filter: prompt appears at the bottom:

Let’s filter for accounts with “checking” in their name. Type checking and press RETURN:

Now only assets:checking is shown.
(And its parent assets account, because we’re in tree mode):

Press RIGHT to see the register for the assets account.
Note, while there are no transactions directly referencing the assets account,
the transactions of its subaccount assets:checking are shown, because we’re in tree mode.
(In flat mode, they wouldn’t be.
That’s what the T:flat(-subs)/tree(+subs) in the help line is about.):

On a register screen, the SHIFT-DOWN/SHIFT-UP keys shrink/expand the time period being shown.
By default, the register shows all of the account’s transactions.
Hold down SHIFT and press the DOWN arrow once. Not much has changed,
but now the heading says “in 2020”
(at least, if you are doing this in the year 2020..).
The report period has shrunk to “the current year”:

Press SHIFT-DOWN again. Now it says “in 2020q1”
(at least, if you are doing this in the first quarter of 2020..).
The report period has shrunk to “the current quarter of the current year”:

Press SHIFT-DOWN again. The next smaller period is “the current month”,
and (at least, if you are doing this in March, 2020..)
the heading now says 2020/03, and now we can see that only 2
of the transactions are in March:

When the report period has been shrunk in this way,
the SHIFT-LEFT/SHIFT-RIGHT keys move backward/forward in time.
So pressing SHIFT-LEFT now should take you to the previous month (2020-02).
There are no transactions in this month.
(ignore the columns of dots; it’s a display bug):

Press SHIFT-LEFT once more to show the previous month (2020-01),
where we can see the single January transaction:

To expand the report period again, press SHIFT-UP several times,
until the “in ...” suffix disappears from the heading:

Press the ESC (escape) key - this is another way to return to the top screen,
and also clears any filter query.

Pressing the ? key shows a more detailed help dialog.
To close it, press ? again, or ESC.

While the help dialog is open, you can press p, m or i
to see the hledger-ui user manual in several offline formats
(useful if you can’t access the web version).
The formats are: plain text (p), using your $PAGER for scrolling if possible:
Unix man (m):
or GNU info (i):
Press q to return to hledger-ui.
Tutorial: hledger-web
hledger-web is hledger’s web browser-based UI. It’s probably the easiest way to get started with hledger. Eg on windows, you can download and unpack hledger.zip and double click on hledger-web.exe. Screenshots below!
Check your installation
Open a terminal or command prompt and check your hledger-web version. It should be reasonably up to date. This doc was last tested with:
$ hledger-web --version
hledger-web 1.17.1
If this fails, check Installing for install and setup tips.
Start hledger-web
Normally, you start hledger-web by running hledger-web in a
terminal, with no arguments. Browsing to the executable file and
double-clicking on it can also work.
Normally, this will start the web app, making it accessible only from your local machine.
For this tutorial, to follow the steps/screenshots below and avoid disturbing any existing data, we’ll start hledger-web with a new temporary journal file:
$ mkdir tmp
$ echo > tmp/.hledger.journal
$ hledger-web -f tmp/.hledger.journal
It will print a startup message and keep running, logging any web requests received:
22/Mar/2020:17:34:19 -0700 [Info#yesod-core] Application launched @(yesod-core-1.6.17.2-1LbNd7zV6uz551mHDWJO3b:Yesod.Core.Dispatch src/Yesod/Core/Dispatch.hs:163:11)
Serving web UI and API on 127.0.0.1:5000 with base url http://127.0.0.1:5000
This server will exit after 2m with no browser windows open (or press ctrl-c)
Opening web browser...
127.0.0.1 - - [22/Mar/2020:17:34:21 -0700] "GET / HTTP/1.1" 303 0 "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0"
127.0.0.1 - - [22/Mar/2020:17:34:21 -0700] "GET /journal HTTP/1.1" 200 - "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0"
127.0.0.1 - - [22/Mar/2020:17:34:23 -0700] "GET /static/js/typeahead.bundle.min.js HTTP/1.1" 200 - "http://127.0.0.1:5000/journal" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0"
...
You can leave this terminal in the background, or minimised, but don’t quit it (unless you want the web app to quit).
And, your web browser should open, showing the UI. If not, open it yourself and browse to the url shown, ie http://127.0.0.1:5000 (and let us know).
A quick tour
Here’s the “home” screen, showing the empty default journal. Or if your journal already contained transactions, you’ll see them listed. “General Journal” means the list of transactions, basically.
Let’s hide the sidebar, for a simpler UI. Pressing the s key should do it
(if not, try again after clicking somewhere on the page, and let us know):

Let’s record some transactions. Press the a key, or click “Add a transaction”, to bring up the add form.
You may need to click or press TAB to focus the Date field:

We’ll copy the sample transactions from the 5-minute quick start.
Press TAB to advance to each next field.
Amount2 can be left blank, or you can fill in $-1234 if you prefer:

Press RETURN or click the add button to save the transaction:

Press a again to add the next transaction.
As you type “assets:checking”, you’ll see there’s some autocompletion available, since we have used that account before:

Finish the transaction, and press RETURN:

Press a and enter the final transaction.
This one has three postings (we spent $100 on groceries and also withdrew $40 as cash).
As before, you can optionally leave one of the amounts blank:

After pressing RETURN. The journal now shows three transactions, with the most recent at the top:

Now, press s to show the sidebar again.
We can see it shows the account hierarchy, and each account’s balance, based on the transactions entered so far:

Clicking on, eg, checking shows the Register for that account -
a list of the transactions affecting this account, and their running total,
and a chart of that total over time:

Press j, or click Journal, to return to the General Journal page showing everything:

Let’s try filtering these transactions with a query.
Type food in the search field and press RETURN.
Now we see just the transaction involving a food account:

Click the x button or click Journal or press j to see all data again.
You can see quick help by pressing ?, or clicking the ? button near the search field.
More help is available in the hledger-web and other manuals, of course:

That’s mostly it! Although there are basically just two screens, with hledger’s full query language available (and with the general flags available on the command line), you can do a surprising amount with hledger-web. You can also change permissions to enable edit/upload/download access.
Tutorial: Import CSV data
hledger has a powerful CSV converter built in. After saving a few declarations in a “CSV rules file”, it can read transactions from almost any CSV file. This is described in detail in the hledger manual, but here are some quick examples.
Say you have downloaded this checking.csv file from a bank for the first time:
"Date","Note","Amount"
"2012/3/22","DEPOSIT","50.00"
"2012/3/23","TRANSFER TO SAVINGS","-10.00"
Create a rules file named checking.csv.rules in the same directory.
This tells hledger how to read this CSV file. Eg:
# skip the headings line:
skip 1
# use the first three CSV fields for hledger's transaction date, description and amount:
fields date, description, amount
# specify the date field's format - not needed here since date is Y/M/D
# date-format %-d/%-m/%Y
# date-format %-m/%-d/%Y
# date-format %Y-%h-%d
# since the CSV amounts have no currency symbol, add one:
currency $
# set the base account that this CSV file corresponds to
account1 assets:bank:checking
# the other account will default to expenses:unknown or income:unknown;
# we can optionally refine it by matching patterns in the CSV record:
if (TO|FROM) SAVINGS
account2 assets:bank:savings
if WHOLE FOODS
account2 expenses:food
You can print the resulting transactions in any of hledger’s output formats:
$ hledger -f checking.csv print
2012-03-22 DEPOSIT
assets:bank:checking $50.00
income:unknown $-50.00
2012-03-23 TRANSFER TO SAVINGS
assets:bank:checking $-10.00
assets:bank:savings $10.00
Or run reports directly from the CSV:
$ hledger -f checking.csv bal
$40.00 assets:bank:checking
$10.00 assets:bank:savings
$-50.00 income:unknown
--------------------
0
Or import any new transactions, saving them into your main journal:
$ hledger import checking.csv --dry-run
; would import 2 new transactions from checking.csv:
2012-03-22 DEPOSIT
assets:bank:checking $50.00
income:unknown $-50.00
2012-03-23 TRANSFER TO SAVINGS
assets:bank:checking $-10.00
assets:bank:savings $10.00
$ hledger import checking.csv
imported 2 new transactions from checking.csv
hledger import ignores transactions it has seen before, so it’s safe
to run it repeatedly. (It creates a hidden .latest.checking.csv file
in the same directory. If you need to forget the state and start over,
delete this.)
Customize the default “unknown” accounts
When converting CSV, hledger uses the account names income:unknown
and expenses:unknown as defaults. Normally when you see these, you
will want to add CSV rules to set a more specific account name.
But you may want to change these defaults, eg into your language.
Method 1: You can add rules something like these, as the first account2 rules:
# set account2 to this:
account2 Revenues:Misc
# change it to Expenses:Misc if the csv "amount" field contains a minus sign:
if %amount -
account2 Expenses:Misc
# override it with more specific rules below...
Method 2: You can use –alias options to rewrite those account names. With hledger 1.20+:
$ hledger -f checking.csv --alias income:unknown=Income:Misc --alias expenses:unknown=Expenses:Misc print
2012-03-22 DEPOSIT
assets:bank:checking $50.00
Income:Misc $-50.00
2012-03-23 TRANSFER TO SAVINGS
assets:bank:checking $-10.00
assets:bank:savings $10.00
See also
Full documentation of CSV conversion, and more rules examples, can be found in the hledger manual.
A collection of CSV rules can be found in examples/csv/ in the main hledger repository. There is also a Makefile to help manage and import multiple CSV files. I keep journals, csv files and csv rules in one finance directory; the makefile moves downloaded csv files there and imports them. The procedure could be:
- In browser: download one or more CSVs manually
- In terminal:
make -C ~/finance Import
There are many other CSV conversion tools (nine CSV->*ledger tools at last count), linked at plaintextaccounting.org -> data import/conversion.
Visualising discussion 2023
Some notes excerpted from https://www.reddit.com/r/plaintextaccounting/comments/121ka8m/how_do_you_visualize_drill_down_your_financial.
Allaman
…tracking my financial data on a very fine-grained level. My only concern is, that I treat this only as a “data sink” and I do not reason over my rather sophisticated data I gathered over the years. One reason is, that I am missing an accessible approach to visualizing my data without much effort. Especially, I miss a method to drill down or compare certain categories / timeframes.
I am aware of subcommands like reg and bal or plotting with, but it always felt cumbersome for me to explore my data this way. And I say this as a “shell power user”.
Can you please share your approach to interactively visualizing, exploring and analyzing your data?
gumnos
I tend to use ledger csv to export subsets of interest to a CSV file and then can import that into a spreadsheet (whether that’s Excel, Libre Office, or Gnumeric or even gnuplot) to get a more visual representation.
I’m usually most interested in trend-lines of how much certain costs are growing. I just did one for our grocery-spend, confirming that our previously-fairly-consistent costs had gone up notably in the last year.
I’ve also occasionally done the ledger csv export and imported it into a sqlitedatabase so I can run queries across it more easily. I do complex SQL as part of $DAYJOBso I tend to query with it naturally, unlike ledger/hledger command-line querying syntax which hurts my brain and I have to consult docs
taviso
I just use a Makefile, make spending in my ledger directory generates pretty graphs and tables.
It works pretty well for me, there’s just a one-time setup of customizing the report to your liking. If you use xterm, you can generate graphs with sixels and see them directly in the terminal (e.g. set terminal sixel in gnuplot).
sm
I have exactly this problem (and I have been doing this since 2006!). Thanks for describing it so well. Hopefully I am paraphrasing it correctly:
How to achieve more big-picture clarity and actionable insight from our hledger(/PTA) data with less effort ?
I would really love if we collectively can make some progress on this.
Speaking for myself:
Things tried
- hledger-chart, bitrotted old charting prototype
- various third party charting tools on hledger CSV output
- a big org babel outline with many embedded reports, updatable/viewable in emacs or exportable as markdown/HTML (notebook interface for reports, AKA “reporter’s notebook”)
- nightly/weekly reports generated and emailed by cron
Things I currently do
- a flurry of incremental CLI reporting when I get motivated to find out specific things
- personalised scripts (mostly using bash) to memoise useful reports and reduce effort
- an always-running time tracking status report in a drop-down terminal
- use hledger-ui for fast browsing with drill-down and period stepping (SHIFT-DOWN/UP/LEFT/RIGHT)
Existing things that might help
- built in financial statements: bs, is
- hledger activity command: hledger CLI’s only built in chart
- hledger-web’s built-in account balance chart
- hledger-plot addon https://hledger.org/scripts.html#hledger-plot
- hledger-vega scripts https://hledger.org/charts.html#hledger-vega
- other notes at https://hledger.org/charts.html
- exporting to CSV (with –layout=tidy, https://hledger.org/dev/hledger.html?highlight=tidy#data-layout) and using spreadsheet charts
- other software https://hledger.org/cookbook.html#other-software, such as>
- exporting to Beancount and using Fava https://hledger.org/beancount.html
- SQlite https://hledger.org/sqlite.html
- Ultorg https://hledger.org/ultorg.html
- Obsidian https://hledger.org/obsidian.html
- Finances 2 https://hochgatterer.me/finances
- Prudent https://prudent.me
- mobile apps with reports/charts, https://hledger.org/mobile.html
- Paisa! https://hledger.org/paisa.html
- Jupyter or other general notebook interfaces
Other things that might help
- built in easy charts that display in the CLI, TUI, or WUI without extra setup
- more / more useful built in financial reports, beyond bs / is
- cloning the reports (at least) of Fava, Finances 2, or Prudent
- better docs, eg a complete practical reporting guide (hledger.org and/or PTA wiki)
- screencasts, videos, demos
- recurring news post / blogs highlighting contributed real examples/demos
- ruthless focus, pruning, creative destruction
- standardisation
- attracting more devs, writers, power users, accounting/finance know-how
- prioritisation, funding
- tabular reports in hledger-ui, hledger-web
- a more agile/rich GUI app built with flutter or some such
Some missing reports/charts
- income/expense bar chart
- asset/liability balance line chart
- conventional cashflow report
- built in lots report
- built in easy investment performance reports
- invoice aging report
Some obstacles
- tool discovery, installation, setup, learnability, usability, reliability
- generality - spanning enough needs, skill levels, platforms, data formats to attract a dev/userbase > 1
- technical debt
- bookkeeping debt
- information debt, docs debt
- complexity management, cognitive overload, maintaining working memory
- discipline, consistency, long term focus
edk99
For text report, create a directory with multiple “args” file. Then a script to generate all the reports in 1 pdf file. You shouldn’t ask for a specific report, let all be generated and see those you are interested. You set it once and just generate from time to time.
For charting, I use superset. It is a good solution if you have a server, but a bit difficult to install. You can use hledger2psql to convert the journal to a database and you can use the docker-compose file included to install with one command.
If you don’t have or don’t like or don’t want to have a server visualization platform installed as Superset, you can use hledger-plot from hledger-utils. It is easier to install and you don’t need a server, but a bit more complicated to create charts in my opinion.
phblj
coming from the parallel universe of beancount… Active analysis is a pain. You can get the data, but there aren’t good built-in tools for exploration. That said, when I do have a particular question I’m trying to answer I try to spend the extra time to build it into a fava extension. After a few years I have a set of commonly-used analyses that are at least a jumping off point for whatever I’m interested in looking into next.
ananthakumaran
I ended up building a web GUI on top of the ledger. I reduced the scope to a single country to better fit my personal usage. https://github.com/ananthakumaran/paisa
ydant
I use beancount, so Fava is my primary browsing interface, but it definitely has a lot of room for improvement. I think it’s also good to have some canned reports you can quickly look at all at once, rather than having to navigate around a UI.
So the main thing I do is have a “family dashboard” Google Sheet for the key data-points my wife wants to see / that we regularly discuss.
It’s really easy to write scripts to import data into a Google Sheet, and as long as you structure your formulas, pivot tables, and charts correctly (using open-ended ranges to account for more data being added by future imports of data), you can dump new data in, and have all of the summaries update automatically.
This approach is basically what Tiller does, as a paid service, although I’m proposing just putting high level data into the Google Sheet (e.g. category expenses, daily/monthly/weekly snapshots, not individual transactions).
Allaman
I wrote a blog post describing my approach to this topic.
[export to SQLite and build adhoc charts with Metabase]
Workflows
There are lots of ways to use hledger; here is an overview. Of course you can mix and match these. Also remember always to back up your files regularly to safeguard your data.
Basic workflows
Command line
At a terminal prompt, run hledger add and follow the interactive prompts to enter transactions.
It will store data in its default location.
Run hledger to list commands to try.
Eg, run
hledger bs to see your account balances (a balance sheet), and
hledger is to see your income and expenses (an income statement).
Tutorial: hledger add describes this process, and hledger’s basic concepts and file format, step by step.
You might want to skim through this one even if you don’t plan to use hledger add.
Web UI
This is the easiest hledger workflow, if it works for you:
Download and run hledger-web, eg by double-clicking on it.
It should open in your web browser.
Use the add form to add transactions.
It will store data in its default location.
(So you’ll see your transactions next time you run it.)
Tutorial: hledger-web describes this in more detail.
Terminal UI
Use hledger add once to create a journal file with an opening balances transaction.
Now run hledger-ui to view account balances.
Use the onscreen help to get around and discover the controls.
Eg, press a to add new transactions.
Tutorial: hledger-ui describes this setup in more detail.
Text editor
Open your preferred text editor and create a journal file, either .hledger.journal in your home directory,
or somewhere else like ~/finance/2023.journal (and set the LEDGER_FILE environment variable).
Create transactions by hand using journal file format.
Once you have a few, you can copy/paste them to make more.
When you want more assistance, set up an editor mode.
Here’s an example:
; $HOME/.hledger.journal
2020-01-01 opening balances
assets:checking $1234
equity
2020-03-15 client payment
assets:checking $2000
income:consulting
2020-03-20 Sprouts
expenses:food:groceries $100
assets:cash $40
assets:checking
Run hledger in a terminal to see reports, as in the Command line workflow. Eg:
$ hledger bs
Balance Sheet 2020-03-20
|| 2020-03-20
=============++============
Assets ||
-------------++------------
assets || $3134
cash || $40
checking || $3094
-------------++------------
|| $3134
=============++============
Liabilities ||
-------------++------------
-------------++------------
||
=============++============
Net: || $3134
$ hledger is -M
Income Statement 2020-01-01-2020-03-20
|| Jan Feb Mar
=========================++=================
Revenues ||
-------------------------++-----------------
income:consulting || 0 0 $2000
-------------------------++-----------------
|| 0 0 $2000
=========================++=================
Expenses ||
-------------------------++-----------------
expenses:food:groceries || 0 0 $100
-------------------------++-----------------
|| 0 0 $100
=========================++=================
Net: || 0 0 $1900
CSV import
Download CSV files from banks and financial institutions, manually or using tools/services that automate this (ledger_autosync, Plaid, plaid2qif, Tiller etc.) Use hledger’s import command to convert and import the new transactions, and use any of the hledger UIs to see reports.
Tutorial: Import CSV data is a quick tutorial on the importing from CSV part. Some downloading helpers can be found at https://plaintextaccounting.org/#data-importconversion (search for “download”).
Custom workflows
SM
- Export Lots workflow
- hledger workflow 2025
- SM’s 2023 setup
- SM’s 2019 setup
- SM’s time & task dashboard
hledger YouTube Business Template
- Kaylee Beyene’s hledger-youtube-business, a beginner-friendly hledger starter kit for YouTubers and Canadians.
phobo3s Excel
- phobo3s’s Excel-based setup, which calculates FIFO cost basis & gains
Full-fledged hledger
- Dmitry Astapov’s Full-fledged Hledger, a CSV-first workflow tutorial
hledger-flow
- Andreas Pauley’s hledger-flow, a tool implementing a variant of the above
rtrLEDGER
- rotorkunstkultur’s hledger system for chaotic German freelancers, using SKR04 accounts
barrucadu
- Michael Walker’s personal finance setup, with Grafana dashboard
Developer docs
Contributor, developer, and maintainer docs. These aim to describe and communicate the structure, processes and workflows of the hledger project - “the machine that makes the machine”.
These mostly are kept in doc/ in the main hledger repo, and then symlinked into the hledger_site repo for rendering on hledger.org.
- Contributor Quick Start
- Developer FAQ
- Project README
- ROADMAP
- ACHIEVEMENTS
- BENCHMARKS
- CHANGELOGS
- CLIMATE
- CODE
- COMMITS
- CREDITS
- DECISIONS
- DEVWORKFLOWS
- DOCS
- ERRORS
- EXAMPLES
- FILES
- FINANCE
- ISSUES
- JUST-MAKE-SHAKE
- LINKS
- MOCKUPS
- PULLREQUESTS
- REGRESSIONS
- RELEASING
- REPOS
- TESTS
- VERSIONNUMBERS
See also:
- https://deepwiki.com/simonmichael/hledger - an AI-generated presentation of hledger’s user and dev docs. Use with caution, but they look rather useful, as an alternate overview / map. Also you can ask it questions.
Dev notes on joyful.com:
- https://joyful.com/hledger
- https://joyful.com/PTA+transaction+balancing
- https://joyful.com/PTA+lot+tracking
- https://joyful.com/hledger+lot+tracking
- https://joyful.com/hledger+workflow+2025
- https://joyful.com/hledger+project+report+2024
Dev links
Discussion:
https://hledger.org/support
Github:
main repo: https://code.hledger.org
commits, https://ci.hledger.org, https://nightly.hledger.org, https://release.hledger.org
https://bugs.hledger.org, https://wishes.hledger.org, https://regressions.hledger.org, ISSUES
https://prs.hledger.org, https://draftprs.hledger.org, https://readyprs.hledger.org, https://assignedprs.hledger.org, https://unassignedprs.hledger.org, PULLREQUESTS
site repo: https://site.hledger.org
finance repo: https://finance.hledger.org
related repos:
Hackage:
packages:
hledger-lib,
hledger,
hledger-ui,
hledger-web,
hledger-diff,
hledger-iadd,
hledger-interest,
hledger-irr,
*hledger*
diffs:
hledger-lib,
hledger,
hledger-ui,
hledger-web
build status:
hledger-lib,
hledger,
hledger-ui,
hledger-web
reverse deps:
hledger-lib,
hledger,
hledger-ui,
hledger-web
Stackage:
build-constraints.yaml
open hledger-related issues
packages:
hledger-lib,
hledger,
hledger-ui,
hledger-web
versions:
hledger-lib,
hledger,
hledger-ui,
hledger-web
Repology:
quick hledger packaging status,
detailed hledger packaging status,
information for hledger,
all *hledger* packages
Homebrew:
hledger
our homebrew requested installs in last 365 days:
2025: #1632 of 28k, top 6%
2023: #1821 of 24k, top 8%
2020: #1520 of 10k, top 15%
Debian:
source packages:
haskell-hledger-lib,
bugs,
haskell-hledger,
bugs,
haskell-hledger-ui,
bugs,
haskell-hledger-web,
bugs
stable:
hledger,
bugs,
hledger-ui,
bugs,
hledger-web,
bugs
testing:
hledger,
bugs,
hledger-ui,
bugs,
hledger-web,
bugs
unstable:
hledger,
bugs,
hledger-ui,
bugs,
hledger-web,
bugs
all:
*hledger*
Popularity contest install stats:
hledger,
hledger-ui,
hledger-web
Ubuntu:
source packages:
haskell-hledger-lib,
bugs,
haskell-hledger,
bugs,
haskell-hledger-ui,
bugs,
haskell-hledger-web,
bugs
binary packages:
*hledger*
Gentoo:
hledger,
hledger-web,
*hledger*
Fedora:
hledger,
*hledger*,
hledger (package db),
Haskell SIG
Void Linux: package search->hledger
Nix: *hledger*
Sandstorm:
hledger web app & reviews,
issues
Reference: fosskers GHC compatibility chart
Old trello planning board: https://trello.hledger.org
hledger-web demo: https://demo.hledger.org
hledger GHCJS demo: https://hledger.fiatjaf.com, https://github.com/fiatjaf/hledger-web \
Developer FAQ
This is just getting started. It will absorb some of the other Developer docs.
How do I get/build the hledger source ?
$ git clone https://github.com/simonmichael/hledger
$ stack build
You can specify hledger, hledger-ui or hledger-web as an argument to build just that executable.
Please see Install > Build from source for more details and other build methods.
What other repos are there ?
There are three official repos:
- https://github.com/simonmichael/hledger - the main hledger repo, for hledger, hledger-ui and hledger-web. Shortcut url: https://code.hledger.org
- https://github.com/simonmichael/hledger_site - the hledger.org website
- https://github.com/simonmichael/hledger_finance - the hledger project’s financial ledger
And third-party add-ons and tools (hledger-iadd, hledger-utils, full fledged hledger, hledger-flow, etc.) have their own repos.
How do I run a build in place ?
After building with stack,
$ stack exec -- hledger [ARGS] # or hledger-ui, hledger-web
Or after building with cabal,
$ cabal exec -- hledger [ARGS]
How do I install a build in PATH ?
$ stack install
This installs the hledger executables to ~/.local/bin. You should have this directory configured in $PATH.
Or you can install to another directory with --local-bin-path.
It builds the executables first if needed; see Install > Build from source for more about building.
You can specify hledger, hledger-ui or hledger-web as an argument to build/install just that executable.
If you use cabal, it has a similar command; the argument is required.
It will install executables to ~/.cabal/bin:
$ cabal install all:exes
How do I build/run with ghc-debug support ?
You might need to stop background builders like HLS, to avoid a fight over the build flag (in VS Code, run the command “Haskell: Stop Haskell LSP server”).
Then build lib and executable(s) with the ghcdebug flag:
$ stack build --flag='*:ghcdebug'
When the build is right, –version should mention ghc-debug:
$ stack exec -- hledger --version
... with ghc debug support
And when you run at debug level -1, -2 or -3 the output should mention ghc-debug:
$ hledger CMD --debug=-1 # run normally, and listen for ghc-debug commands
$ hledger CMD --debug=-2 # pause for ghc-debug commands at program start (doesn't work)
$ hledger CMD --debug=-3 # pause for ghc-debug commands at program end (doesn't work)
Starting ghc-debug on socket: ...
Now in another window, you can run ghc-debug-brick and it will show the hledger process (until it ends). Press enter to connect. If it fails,
- you might need to clear out stale sockets:
rm -f ~/.local/share/ghc-debug/debuggee/sockets/* - you might need to kill stale hledger processes:
pkill -fl hledger - with –debug=-2 or -3 it fails with
rts_resume: called from a different OS thread than rts_pause, so use –debug=-1 instead. This works best with the long-running hledger-ui or hledger-web; with hledger, you’ll need a big enough data file so that you have time to connect before it finishes. Once connected, presspto pause the program.
At this point, you can explore memory/profile information, save snapshots, resume execution, etc.
Or, instead of ghc-debug-brick you can write a ghc-debug-client script to extract specific information.
ACHIEVEMENTS
Achievements
Some project achievements to unlock. Being a software maintainer means unending toil in mostly-obscurity, so you’ve got to seek out the fun where you can!
1.0 releasepackaged in major distros, binaries availablepretty good reference docs100 committers1k github starsamong top 50 starred haskell projects on githubdiscussed on Hacker Newsmatch ledger IRC channel sizeamong top 40 starred haskell projects on githubmultiple people providing supportpretty good tutorial docspretty good cookbook docs100 IRC chatters2k github starsin top 30 github starred haskell projectsin top 20 github starred haskell projects- consistently mentioned in “good Haskell apps” discussions
- 200 committers
- 2 low regression releases in a row (no new regressions found in 2 consecutive major releases plus 3 months)
- best in class for investment tracking
- 2.0 release
- match ledger speed
- 3 low regression releases in a row
- match ledger committers
- match beancount stars
- match ledger stars
- in top 10 github starred haskell projects
- 4 low regression releases in a row
Stars
-
Github stars history, with Ledger and Beancount
| all projects | non programming tools | |
|---|---|---|
| 2026 | #11 | #4 (after pandoc, simplex chat, kmonad) |
| 2025 | #19 | #5 (after pandoc, simplex chat, kmonad, cardano) |
| 2023 | #32 | |
| 2022 | #34 | |
| 2020 | #36 | |
| 2018 | #53 | |
| 2017 | #54 | |
| 2016 | #71 |
- Top 100 Github-starred Haskell projects (manually updated)
See also: ROADMAP
Benchmarks
Benchmarks are standard performance measurements,
which we define using bench declarations in cabal files.
There is one in hledger.cabal,
with related code and data files in hledger/bench/.
To run the standard hledger benchmark, use stack bench hledger.
This installs haskell dependencies (but not system dependencies) and rebuilds as needed,
then runs hledger/bench/bench.hs,
which by default shows quick elapsed-time measurements for several operations on a standard data file:
$ stack bench hledger
NOTE: the bench command is functionally equivalent to 'build --bench'
...
hledger-0.27: benchmarks
Running 1 benchmarks...
Benchmark bench: RUNNING...
Benchmarking hledger in /Users/simon/src/hledger/hledger with timeit
read bench/10000x1000x10.journal [1.57s]
print [1.29s]
register [1.92s]
balance [0.21s]
stats [0.23s]
Total: 5.22s
Benchmark bench: FINISH
bench.hs has some other modes, which you can use by compiling and running it directly.
--criterion reports more detailed and dependable measurements, but takes longer:
$ cd hledger; stack exec -- ghc -ibench bench/bench && bench/bench --criterion
...
Linking bench/bench ...
Benchmarking hledger in /Users/simon/src/hledger/hledger with criterion
benchmarking read bench/10000x1000x10.journal
time 1.414 s (1.234 s .. 1.674 s)
0.996 R² (0.989 R² .. 1.000 R²)
mean 1.461 s (1.422 s .. 1.497 s)
std dev 59.69 ms (0.0 s .. 62.16 ms)
variance introduced by outliers: 19% (moderately inflated)
benchmarking print
time 1.323 s (1.279 s .. 1.385 s)
1.000 R² (0.999 R² .. 1.000 R²)
mean 1.305 s (1.285 s .. 1.316 s)
std dev 17.20 ms (0.0 s .. 19.14 ms)
variance introduced by outliers: 19% (moderately inflated)
benchmarking register
time 1.995 s (1.883 s .. 2.146 s)
0.999 R² (0.998 R² .. NaN R²)
mean 1.978 s (1.951 s .. 1.995 s)
std dev 25.09 ms (0.0 s .. 28.26 ms)
variance introduced by outliers: 19% (moderately inflated)
benchmarking balance
time 251.3 ms (237.6 ms .. 272.4 ms)
0.998 R² (0.997 R² .. 1.000 R²)
mean 260.4 ms (254.3 ms .. 266.5 ms)
std dev 7.609 ms (3.192 ms .. 9.638 ms)
variance introduced by outliers: 16% (moderately inflated)
benchmarking stats
time 325.5 ms (299.1 ms .. 347.2 ms)
0.997 R² (0.985 R² .. 1.000 R²)
mean 329.2 ms (321.5 ms .. 339.6 ms)
std dev 11.08 ms (2.646 ms .. 14.82 ms)
variance introduced by outliers: 16% (moderately inflated)
--simplebench shows a table of elapsed-time measurements for the commands defined in bench/default.bench.
It can also show the results for multiple h/ledger executables side by side, if you tweak the bench.hs code.
Unlike the other modes, it does not link with the hledger code directly, but runs the “hledger” executable found in $PATH (so ensure that’s the one you intend to test).
$ cd hledger; stack exec -- ghc -ibench bench/bench && bench/bench --simplebench
Benchmarking /Users/simon/.local/bin/hledger in /Users/simon/src/hledger/hledger with simplebench and shell
Using bench/default.bench
Running 4 tests 1 times with 1 executables at 2015-08-23 16:58:59.128112 UTC:
1: hledger -f bench/10000x1000x10.journal print [3.27s]
1: hledger -f bench/10000x1000x10.journal register [3.65s]
1: hledger -f bench/10000x1000x10.journal balance [2.06s]
1: hledger -f bench/10000x1000x10.journal stats [2.13s]
Summary (best iteration):
+-----------------------------------------++---------+
| || hledger |
+=========================================++=========+
| -f bench/10000x1000x10.journal print || 3.27 |
| -f bench/10000x1000x10.journal register || 3.65 |
| -f bench/10000x1000x10.journal balance || 2.06 |
| -f bench/10000x1000x10.journal stats || 2.13 |
+-----------------------------------------++---------+
bench’s –simplebench mode is based on a standalone tool, tools/simplebench.hs. simplebench.hs is a generic benchmarker of one or more executables (specified on the command line) against one or more sets of command-line arguments (specified in a file). It has a better command-line interface than bench.hs, so you may find it more convenient for comparing multiple hledger versions, or hledger and ledger. Eg:
$ stack exec -- ghc tools/simplebench
[1 of 1] Compiling Main ( tools/simplebench.hs, tools/simplebench.o )
Linking tools/simplebench ...
$ tools/simplebench -h
tools/simplebench -h
simplebench: at least one executable needed
bench [-f testsfile] [-n iterations] [-p precision] executable1 [executable2 ...]
Run some functional tests with each of the specified executables,
where a test is "zero or more arguments supported by all executables",
and report the best execution times.
-f testsfile --testsfile=testsfile file containing tests, one per line, default: bench.tests
-n iterations --iterations=iterations number of test iterations to run, default: 2
-p precision --precision=precision show times with this precision, default: 2
-v --verbose show intermediate results
-h --help show this help
Tips:
- executables may have arguments if enclosed in quotes
- tests can be commented out with #
- results are saved in benchresults.{html,txt}
cd hledger; $ ../tools/simplebench -f bench/default.bench hledger ledger
Using bench/default.bench
Running 4 tests 2 times with 2 executables at 2015-08-24 04:24:37.257068 UTC:
Summary (best iteration):
+-----------------------------------------++---------+--------+
| || hledger | ledger |
+=========================================++=========+========+
| -f bench/10000x1000x10.journal print || 3.24 | 0.43 |
| -f bench/10000x1000x10.journal register || 3.80 | 3.48 |
| -f bench/10000x1000x10.journal balance || 2.05 | 0.18 |
| -f bench/10000x1000x10.journal stats || 2.10 | 0.19 |
+-----------------------------------------++---------+--------+
Finally, for quick, fine-grained performance measurements when troubleshooting or optimising, I use dev.hs.
Changelogs
See also RELEASING.md.
Update master changelogs often
Before release, and preferably daily/weekly as well:
- during normal development, do this only in master branch
- during release preparation.. unclear; keep the other branch’s changelog in mind, they need to be synced at some point. Prioritise [minor] release branch’s changelog ?
./Shake changelogs- edit the new changelog items
- open the five changelogs
- in emacs:
C-x d */CHANGES.md,(, split into 5 columns, open each one, narrow from last release heading to top
- in emacs:
- in each changelog
- paste section headings from top comment if needed
- process new items, from top-most section heading upward
- drop boring items (changes not visible to end/API users, followups to other new items, minor doc updates..)
- drop duplicated items/content (mostly between cli and lib)
- move to top of appropriate changelog & section (create section when needed; consider a second pane to reduce scrolling)
- drop prefixes, edit texts, check/adjust links
- (in cli/web/ui, move any “API changes” parts to lib ?)
- proof read/clean up all changelogs (Obsidian works well)
./Shake changelogs -c
- open the five changelogs
Update release & master changelogs at release time
Before release, after cherry-picking changes from master to the release branch:
- check out master in a separate working copy
- for each changelog
- open master & release changelog in side-by-side windows
- copy all appropriate new content from master to release
- in release changelog, remove any previous prerelease heading, add a new release heading
- in release ui/web changelogs, add/update “uses hledger X.Y” item if needed
- if this is a major release, or a minor release with notable project updates, copy to master changelogs:
- the ui/web “uses hledger X.Y”
- the new release heading
- commit changelog updates in both working copies
- pull the master changelog updates back to main working copy
- destroy temporary working copy, emacs buffers
Update release notes
- add a new section in site/src/release-notes.md
- copy/rewrite/summarise package changelogs
- note any other items of interest
- list release contributors
- write release summary
Old notes
Changelogs started using markdown from 1.0 onward. Should make consistent.
Changelogs started mentioning committer names from 2017/1, for hledger-ui-1.1.1 (because they won’t appear on the release notes). Could do it just for minor releases but might as well do it for all. Could do it for past releases but no pressing need.
In site/release-notes.md, we stopped mentioning minor releases around 0.27. The old minor releases should probably be removed or promoted to the same heading level as major releases.
Climate impact
Most human endeavours should maintain and publish an environmental cost/benefit report. “Cost” means some environmental impact such as resources used (energy, water, land..)
or undesirable waste generated (carbon dioxide, toxins, heat, noise, harm..)
Here is the beginnings of one for the hledger project, with a catchier name.
Development
Costs related to developing hledger come from:
- developer machine activity
- recompiling, linking, building docs, browsing, running IDEs etc.
- test machine activity
- CI workers recompiling on push, on PR updates etc.
- source code hosting
- VCS operations, file and artifact hosting, rich web UIs, issue tracking, forks, notifications (github etc.)
- public websites and services
- hledger.org doc hosting, updating
- demo.hledger.org hosting
- discussions (matrix, irc, reddit, hacker news..)
- media (youtube..)
- project analysis (libhunt..)
Benefits:
- providing effective accounting tools to people and organisations who would be less effective/less empowered by other tools, potentially unlocking further eco accounting activity and successes
- potentially help advance best practices and community know-how by sharing useful techniques, tools, processes, architecture, patterns
Delivery
Costs related to packaging and shipping hledger to end-users. Related to development, but broken out separately here. This includes:
- Github artifact building / additional testing beyond standard dev tests
- Hackage hosting/maintenance
- Stackage hosting/maintenance
- Hosting/maintenance in all the other packaging systems and distributions on the Install page
- Download/install/cloning/build work done by user machines
Benefits:
- Roughly speaking, more work done on packaging and delivery means more users able to try out and benefit from the software, magnifying the Development benefits above
Your hledger usage
Costs from your individual/organisational usage of hledger, other than Delivery costs above, may include:
- Machine usage every time you run hledger
- Hardware upgrade/purchase pressure to satisfy memory/disk space requirements
- Personal and online machine usage and human time while learning, discussing, configuring and maintaining hledger and PTA
Benefits:
- Hopefully, more effective accounting and eco accounting, reducing waste and increasing personal/organisational solvency, success, empowerment and positive impact
Stats
Some useful statistics to guestimate:
- Yearly cost
- Yearly cost trend
- Cumulative cost
- Predicted lifetime cost
- Comparison with alternate solutions
Code
hledger is a suite of applications, tools and libraries.
The main hledger code repository is github.com/simonmichael/hledger
(shortcut url code.hledger.org).
There are also various hledger add-ons maintained as separate projects with their own repos.
hledger packages
Within the main repo, there are a number of separate cabal packages, making it easier to pick and choose parts of hledger to install or to package. They are:
hledger-lib
Core data models, parsing, standard reports, and utilities. Most data types are defined in Hledger.Data.Types, while functions that operate on them are defined in Hledger.Data.TYPENAME. Under Hledger.Read are parsers for the supported input formats. Data files are parsed into a Journal, which contains a list of Transactions, each containing multiple Postings of some MixedAmount (multiple single-CommoditySymbol Amounts) to some AccountName. When needed, the Journal is further processed to derive a Ledger, which contains summed Accounts. In Hledger.Reports there are standard reports, which extract useful data from the Journal or Ledger.
Here’s a diagram of the main data model:
hledger
hledger’s command line interface, and command line options and utilities for other hledger tools.
Try tracing the execution of a hledger command:
- Hledger.Cli.Main:main parses the command line to select a command, then
- gives it to Hledger.Cli.Utils:withJournal, which runs it after doing all the initial parsing.
- Parsing code is under hledger-lib:Hledger.Read, eg Hledger.Read.JournalReader.
- Commands extract useful information from the parsed data model using hledger-lib:Hledger.Reports, and
- render in plain text for console output (or another output format, like CSV).
- Everything uses the data types and utilities from hledger-lib:Hledger.Data and hledger-lib:Hledger.Utils.
hledger-ui
A terminal interface.
hledger-web
A web interface. hledger-web starts a web server built with the yesod framework, and (by default) opens a web browser view on it. It reads the journal file(s) at startup and again whenever they change. It can also write (append) new transactions to the journal file.
There are two main views, which can be filtered with queries:
-
/journal, showing general journal entries (like
hledger print) -
/register, showing transactions affecting an account (slightly different from hledger’s register command, which shows postings).
There is also:
- a sidebar (toggled by pressing
s) showing the chart of accounts (likehledger balance) - an add form for adding new transactions (press
a) - a help dialog showing quick help and keybindings (press
hor click ?)
Most of the action is in
- config/routes
- templates/default-layout-wrapper.hamlet
- Foundation
- Handler.*
- static/hledger.js
- static/hledger.css
Handler module and function names end with R, like the yesod-generated route type they deal with.
Dynamically generated page content is mostly inline hamlet. Lucius/Julius files and widgets generally are not used, except for the default layout.
Here are some ways to run it during development:
yesod devel: runs in developer mode, rebuilds automatically when config, template, static or haskell files change (but only files in the hledger-web package):
$ (cd hledger-web; yesod devel)
-
yesod-fast-devel may be a good alternative, also reloads the browser page
-
stack ghci: runs the server in developer mode from GHCI. Changes to static files like hledger.js will be visible on page reload; to see other changes, restart it as shown.
$ (cd hledger-web; stack ghci hledger-web)
hledger-web> :main --serve # restart: ctrl-c, :r, enter, ctrl-p, ctrl-p, enter
just ghci-web: runs the server in developer mode from GHCI, also interprets the hledger-lib and hledger packages so that :reload picks up changes in those packages too:
$ just ghci-web
ghci> :main --serve
(This rule also creates symbolic links to hledger-web’s config, messages, static and templates
directories, needed in developer mode, so it can run from the top directory. This may not work on Windows.)
Quality
Relevant tools include:
- unit tests
- functional tests
- performance tests
- documentation tests
- ui tests (manual)
- installation tests
- code reviews
Code review
- Code review party 2014/7/21-25: discussion
- Dev sprint/party 2015/10/10: discussion
Code docs
Haddock comments are the standard way of attaching docs (and sometimes small tests) to a haskell definition. They are used pervasively in the hledger codebase.
From #2222:
I tend to add a line of english description to most things. They can be a big help to someone else debugging/improving code later.
I use Haddock but I do not like to tell obvious things in Haddock comments. (And if there are surprising things I try to eliminate the surprises instead.)
I hear you! This is of course a recurring debate among programmers.
I agree that obviously redundant haddocks that add no value are to be avoided.
In the hledger codebase, there’s no hard requirement for haddocks. And sometimes the bar can be lower, eg in single-purpose less-frequently-developed modules like FODS.hs.
But I believe it’s always worth at least considering them when adding code, and it’s something I consider when reviewing code. I’ll give some reasons why I think so, for the record (and maybe they’ll persuade you just a little).
Haddocks help guide and anchor the developer while writing or changing code. They are the cheapest kind of doc, spec and test suite. They also create a place to add actual doctests, now or later.
Haddocks can be helpful to contributors who are not expert haskellers, which happens quite often in the hledger project. The human language descriptions can complement the code, reducing cognitive effort and helping with mental chunking. I think people find the hledger’s code, with its pervasive haddocks, above average in readability. (And I think we’ve had more successful contributions as a result.)
Haddocks can also help experienced developers move faster. Code which seems quite clear and obvious when you are writing it is often less obvious to oneself a few weeks or years later. And certainly it can be less obvious to others than we might think. When debugging or coding we are often mentally stretched and we would prefer to conserve brainpower for the main task.
In my experience debugging/writing/changing hledger code,
- Number of times I’ve regretted seeing a haddock comment attached to some code: almost zero.
- Effort to remove an excessive haddock: almost zero.
- Number of times I’ve been trying to understand some contributed code and haddocks would have saved my time: quite a few.
- Number of times I’ve appreciated code haddocks and found them helpful: many.
- Number of times I’ve found errors or staleness in haddocks: not often.
- Effort to fix wrong haddocks, or to write new ones: usually very low. And if it’s high, it’s usually very worthwhile because it alerts me to a confusion in the code or clarifies my thinking.
hledger is a documentation-driven project, with docs in general being a top priority. I think this is one reason it has held together and kept improving over a long period.
COMMITS
In the hledger project we try to follow certain conventions for commit messages, because good messages lead to good commits => good change docs => easier code review => quicker merging => faster delivery of quality software. We’ll check and help you polish messages as part of CI and code review. (You can also set up a local commit hook, described below.)
Here’s the typical format:
[feat|imp|fix[!]:] topic: Summary
[Longer description when useful]
More precisely:
- Commit messages must begin with one or more prefixes (colon-terminated words), indicating the type and/or topic.
- Commits causing user-visible changes must begin with
feat:,imp:orfix:(feature, improvement, or bugfix). These will be used in release notes. If they are breaking/incompatible changes, usefeat!:,imp!:orfix!:. - To skip CI builds on commits which would normally trigger one, add a
;at the beginning. (Our CI does a lot of work, so you can use this to reduce energy waste and carbon emissions from minor changes. Non-code commits do this automatically.) - Mention any relevant issue numbers, usually parenthesised at the end.
(#NNNN) - Try to write commit messages as changelog/release-note-ready documentation that will tell their intended audience (which might be users, installers, packagers, and/or developers) what they need to know.
Some examples:
feat: accounts: --types shows account types (#1820)imp!: journal: Remove deprecated account type code syntax from account directives.fix: types: Ensure auto postings can match against and be matched by type: queries.tools: commitlint: allow a git "fixup! " prefixdoc: releasing: tweaks
Some possible prefixes:
feat- a new featureimp- an improvement to existing featuresfix- a bugfixdev- a generic developer changeref- refactoringcln- cleanupdoc- documentation-relatedtest- tests-relatedci- continuous integration-related- Any of the standard labels used in the issue tracker.
How to check commits
Before committing, pushing, or merging, run tools/commitlint to check recent commit messages.
(See the script for more ways to select commits.) You can configure your local working copy
to do this automatically, by running just installcommithook.
commitlint also runs automatically on Github to check pull requests.
See also
- https://groups.google.com/g/hledger/c/t14xwHQMJqU/m/9frARXIdAAAJ
- https://conventionalcommits.org
- https://git.savannah.gnu.org/cgit/emacs.git/plain/CONTRIBUTE -> Commit messages
Decisions
A partial list of notable development decisions / design choices..
2022
Replace “transaction price” terminology with “cost”
“Transaction price” never quite stuck. “Cost” is simpler, shorter, more intuitive, consistent with --cost and “cost reporting”, and more distinct from “market price”.
There is an (acceptable) ambiguity: “cost” could mean the @ UNITCOST price attached to the amount, or the total cost when the amount is converted (QUANTITY * UNITCOST).
Status: as of 2023Q1 this has been done in the manuals and is slowly ongoing in the code.
2023
Plugin types
We will document and support where feasible several distinct kinds of plugin, written in haskell or other languages, such as reader, processor, writer, formatter, command. See https://hledger.org/scripting.html#plugin-types.
2025
I think the keyword-first style for directives is right for us (open 2025-01-01 ..., not 2025-01-01 open ...).
It avoids polluting/breaking transaction descriptions, it’s similar to P,
it keeps directives and transactions visually distinct,
and consistently beginning with letters and numbers respectively.
Yes we should support declaring aliases with alias: tags on account directives.
Developer workflows
Get developer tools
Ensure stack is installed
(or if you’re a cabal expert, feel free to use that.)
Ensure git is installed. On Windows, it comes with stack.
Here are some useful optional tools:
justfor automating project tasks- GNU Make for automating tasks in a few subdirectories
watchexecfor re-running commands when files changeghcidgives real-time GHC feedback as you make code changes.shelltestrunnerruns hledger’s functional tests.quickbenchmeasures and reports time taken by commands.hasktagsgenerates tag files for quick code navigation in editors like Emacs and vim.- For browsing and editing Haskell code, popular tools include: Emacs, Vim, IDEA, VS Code, Atom..
Eg:
stack install ghcid shelltestrunner hasktags
git clone https://github.com/simonmichael/quickbench; cd quickbench; stack install # must run in source dir
Get the code
git clone https://github.com/simonmichael/hledger
cd hledger
Review code
- review and discuss new pull requests and commits on github
- build hledger and test the latest changes in your own repo
- read the existing code docs and source
- send feedback or discuss via IRC or mail list
Build in place
See also https://hledger.org/install.html#c.-build-the-development-version .
stack build # hledger hledger-ui ...
This fetches the required GHC version and haskell dependencies from the default stackage snapshot (configured in stack.yaml),
then builds all hledger packages.
This can take a while! To save time, you can build individual packages, eg just the CLI and TUI.
Note stack does not fetch C libraries such as curses or terminfo, which you might need to install yourself, using your system’s package manager. In case of trouble, see download.
If you want to use an older snapshot/GHC for some reason, specify one of the older stack-*.yaml files:
stack --stack-yaml stack8.2.yaml build
Run in place
stack exec -- hledger # ARGS...
stack exec -- hledger-ui # ARGS...
stack exec -- which hledger
Build and install
This builds and also copies the hledger executables to ~/.local/bin or the Windows equivalent
(which you should add to your $PATH).
stack install # hledger hledger-ui ...
Run package tests
Runs any HUnit/doctest/easytest tests defined by each hledger package.
stack test # hledger ...
Run package benchmarks
Runs any performance reports defined by each hledger package.
stack bench # hledger ...
Run quickbench benchmarks
Times the end-user commands in bench.sh using quickbench.
just bench
Run functional tests
Runs the shelltestrunner tests defined in hledger/test/, which test the hledger CLI.
just functest
Run haddock tests
Checks for anything that would break haddock doc generation.
just haddocktest
Checks for the unit-tests embedded in documentation.
just doctest
Simulate Travis tests
Locally runs tests similar to what we run on Travis CI.
just travistest
Test with all supported GHC versions/stackage snapshots
just allsnapshotstest
Use GHCI
GHCI is GHC’s REPL, useful for exploring and calling code interactively.
If you try to run GHCI (or things based on it, like ghcid)
right after cloning the hledger repo, you might see an error about CPP macros, eg like
on #961.
To fix this, build the hledger packages once, eg stack build.
(Or stack build hledger might be enough, depending what you are doing.)
Get a GHCI prompt for hledger-lib:
cd hledger-lib; stack ghci hledger-lib
Changing into the package directory isn’t actually needed, but it enables a custom .ghci script which sets a more useful short prompt.
Get a GHCI prompt for hledger:
cd hledger; stack ghci hledger
Get a GHCI prompt for hledger-ui:
cd hledger-ui; stack ghci hledger-ui
Get a GHCI prompt for hledger-web:
cd hledger-web; stack ghci hledger-web
hledger-web also needs to find some things in its current directory (like the static/ directory). This normally just works, if not please send details.
Add a test
- identify what to test
- choose the test type: unit ? functional ? benchmark ?
- currently expected to pass or fail ?
- figure out where it goes
- write test, verify expected result
- get it committed
Fix a bug or add a feature
- research, discuss, validate the issue/feature on irc/list/bug tracker
- look for related tests, run the tests and check they are passing
- add a test ?
- develop a patch
- include any related issue numbers in the patch name, eg: “fix for blah blah (#NNN)”
- get it committed
Get your changes accepted
Follow the usual github workflow:
- fork the main hledger repo on github,
- git clone it to your local machine,
- git commit, after (?) pulling and merging the latest upstream changes
- git push back to github,
- open a pull request on github,
- follow up on any discussion there.
If you’re new to this process, help.github.com may be useful.
Add yourself to the contributor list
- after getting something into the master branch, read and sign the contributor list & agreement. Or, ask to be added.
- give yourself a high five!
Work on docs
Most docs tasks are handled by Shake.
List Shake rules:
./Shake
Generate man/info/txt manuals (in hledger*/) and embed in hledger executables:
./Shake manuals
stack build
Generate html manuals and the hledger website (in site/_site/):
./Shake website
To remove all files generated by Shake:
./Shake Clean
Use ghcid for watching GHC/GHCI
ghcid is the most reliable and fastest way to see GHC’s feedback, and optionally run tests or a GHCI command, as you edit. We run it via just, for convenience and to watch multiple packages rather than just one. Run just h ghcid to list related rules.
Watch for compile errors in hledger-lib and hledger:
just ghcid
Watch compile errors and the output of some hledger command:
ghcid -c 'just ghci' -T ':main -f a.j bal --budget -N'
Use –file-watch for watching stack
stack’s –file-watch flag will re-run build/test/bench when source files or package.yaml/cabal files change. Eg:
stack test hledger --file-watch
If you find that adding –fast makes this any faster, please update this.
Rerun a single functional test as you change it:
watchexec -w hledger/test/journal/assertions.test just functest -i budget.*19
DOCS
An overview of hledger’s documentation structure and maintenance procedures, for developers and maintainers. (For user documentation, see Docs. For all developer documentation, see Developer docs.)
Documentation structure
2019: out of date, needs update.
Project documentation lives in a number of places:
site/*.mdis the hledger.org website content, which is generated with hakyll[-std] and pandoc- haddock documentation in the code appears on Hackage
- short blurbs: cabal files, module headers, HCAR, GSOC project, ..
doc/notes.orghas some old developer notes- developer reports (profiles, benchmarks, coverage..) in doc/profs, sometimes published at hledger.org/profs
- https://github.com/simonmichael/hledger/tree/master/doc
site/ is now a symlink to the separate hledger_site repo.
See also Shake.hs.
hledger doc files can be divided into several groups:
- Project admin/dev notes not published on the website.
These are kept in this directory (doc/). They include:
doc/finance/ project finances doc/hcar/ Haskell Community and Activities Report entries doc/lib.m4 common macros used in package manuals doc/manpage.* misc. templates for rendering package manuals doc/mockups/ exploratory developer mockups doc/profs/ a place for long-term profiling/performance data - Project doc files required to be in the top directory:
README.md the main project readme, displayed on github LICENSE the default project license - Code/API docs in haskell source files as haddock comments:
hledger*/**/*.hs haddock module and function docs in most source files - Per-package descriptions, readmes, changelogs, and reference manuals.
These are in the respective package directories:
hledger*/package.yaml source for package metadata (description, etc.) hledger*/README package readme, displayed on hackage hledger*/CHANGES package changelog, displayed on hackage hledger*/hledger*.m4.md package manual source file(s) - The project website and additional docs - home page, FAQ, tutorials,
how-tos, developer guide, etc. These are in the site directory:
site/ hledger.org website content, templates, assets
Workflows
Last updated: 2025-09
Compile the Shake script
Shake.hs automates some doc maintenance tasks (complementing Justfile).
Most contributors don’t need to use it, but if you do, compile it like so:
in the hledger repo, run:
$ ./Shake.hs
Update options help
Edit general options definitions in hledger/Hledger/Cli/CliOptions.hs
and command options definitions in hledger/Hledger/Cli/Commands/*.hs.
Update manuals’ content
Updates and fixes for the manuals’ content are welcome and encouraged! They can be committed together with related code changes, or separately.
The manuals have (a) source files, kept in the hledger repo and (b) generated files derived from those. Don’t edit the generated files, such as:
hledger/hledger.mdorhledger-ui/hledger-ui.mdin the hledger reposite/src/1.50/hledger*.mdorsite/src/dev/hledger*.mdin the hledger_site repo
Instead, edit the source files:
hledger/hledger.m4.mdorhledger/Hledger/Cli/Commands/*.mdfor the hledger manualhledger-ui/hledger-ui.m4.mdfor the hledger-ui manualhledger-web/hledger-web.m4.mdfor the hledger-web manual.
(There are a few more source files which change less often; if you click “edit this page” on a recent release manual on the website, you’ll see all source files listed.)
Update manuals’ generated files
Contributors don’t need to do this; usually it’s done periodically by the maintainer. It requires unix tools such as m4, makeinfo and pandoc.
In the hledger repo: first, set current year and month for the man pages:
$ just mandates
Then update the lists of command line options in the manuals’ source files (only as needed, if command line options or option help have changed):
$ stack build && ./Shake cmddocs -c
Then regenerate the text, man, info, and markdown manuals in hledger*/ from their source files:
$ ./Shake manuals -c
Update dev manuals on the website
When updates to manuals’ generated files land in the master branch of the hledger repo on github, the dev manuals on hledger.org will update automatically.
(The manuals on the website are rendered from site/src/VERSION/*.md in the hledger_site repo,
which are symlinked copies of hledger/hledger.md, hledger-ui/hledger-ui.md and hledger-web/hledger-web.md in the hledger repo.)
Update release manuals on the website
Contributors can do this, but doing it the right way is a little complicated; you can also ask the maintainer to do it.
The release manuals on the website are rendered from site/src/1.50/*.md, site/src/1.43/*.md, etc.
These are generated as follows:
In the hledger repo, with the hledger_site repo symlinked as ./site;
for each major release REL that needs updating:
- Cherry pick the manuals’ content updates for REL (not generated files updates) from
mastertoREL-branch - In master, run
just site-manuals-snapshot RELto update the release manuals in the site repo.
When these commits land in the hledger_site repo on github, the release manuals on hledger.org will update automatically.
Add new release manuals to the website
A few extra steps are needed the first time new release’s manuals are added to the site, to update redirects and the version links shown at the top of manuals:
- In the site repo, update and push version numbers in Makefile, site.js, hledger.org.caddy
- On hledger.org, restart the web server
- On cloudflare, purge hledger.org/site.js from the cache
Update hledger binaries with latest docs
This ensures the hledger dev executables are embedding the latest manuals’ generated files, affecting:
- options help displayed by
--help - command docs displayed by
CMD --help - manuals displayed by
help,--info, and--man.
Update the options help, manuals’ content and manuals’ generated files as above, then rebuild:
$ stack build
Update change logs
Changelogs are in **/CHANGES.md (one in each package, and one at top level for the project).
They should ideally be updated continually (at least weekly), in master, taking advantage of fresh memory and context.
At release time they get some extra polish, and are propagated to the release branch.
To update changelogs, in master or in a release branch:
- Add new draft change notes to all changelogs (based on commit messages since the release or commit id mentioned in their first heading):
$ just changelogs - Edit and polish the new change notes.
- Add issue number links, eg with
md-issue-refsmacro. - Commit, eg with
just changelogs -c
Note once the release branch is created, extra care is needed to keep changelogs synced between branches, considering:
- edits to existing change notes
- new change notes, corresponding to code changes cherry picked from master or created in the release branch.
At such times it may be useful to follow this sequence:
- Add/sync code changes to the release branch.
- Update change logs in release branch, based on latest commits there.
- Cherry pick change log updates from the release branch to (that release’s section in) the changelogs in master.
- Update change logs in master, to add any more unreleased commits at the top (assisted by manual cleanup).
Finalise change logs for a release
In the release branch, on the day of release, run this to add release headings and commit:
$ just changelogs-finalise
Update release notes
Release notes are in doc/relnotes.md in the hledger repo (and symlinked as site/src/relnotes.md in the site repo).
They are generated at release time, from the finalised change logs.
Note once the release notes are generated, they should be kept in sync with any late updates to the changelogs (by regenerating them).
To update release notes:
- In the release branch, with change logs finalised, run
$ just relnotes - Review the new release notes. When changes are needed, change the changelogs, then regenerate the release notes as above.
- Add release contributors and github nicks at the bottom.
- Add release highlights at the top.
- Commit.
Update release notes on github
Release notes are uploaded to each github release, with some additional github-specific release docs.
In the release branch, once the corresponding github release is created, after updating release notes:
- If needed, update github release docs in
doc/ghrelnotes. - Push this and the latest release notes to the github release:
$ just ghrel-notes
Update release notes on the website
- Cherry pick the latest release notes (
doc/relnotes.md) from the release branch to master. - Push to the master branch on github. The website’s “Release notes” page will update automatically.
Diagrams
201901 docs reorg (#920, WIP)
https://groups.google.com/forum/#!topic/hledger/t2nVr3zER8Q/discussion
On Oct 26, 2018, at 1:47 PM, Simon Michael [email protected] wrote:
A quick heads-up: I am feeling like stepping back from github wiki, and reorganising our docs like so:
Two repos:
- hledger - code and hard docs
- code and code docs (haddock docs & doctest examples)
- developer docs (READMEs in md or org format)
- product manuals (hledger*/hledger*.m4.md)
- release notes and announcements
- HCAR entries
- hledger-site - website and soft docs
- hledger.org content, resources, site infrastructure
- user cookbook, how-tos, articles
- links to blog posts, discussions etc.
- other resources relating to our web presence/marketing
If you disagree, let’s discuss. Some quick considerations:
- moving docs to the wiki hasn’t affected the contribution rate
- using the wiki increases our dependence on github and makes our work less self-contained and future-proof
- the wiki docs don’t look great, aren’t very flexible, & don’t integrate well with our site & static docs
- using two docs systems increases complexity
- dev docs in the wiki are too far from the code, and compete with READMEs
PS:
- Why not go back to just one repo for everything ? Or if two repos, why not put all docs in one of them ?
Dev docs are most discoverable and maintainable right there in the main repo, ie as READMEs. Likewise for API docs (haddocks) and the reference manuals (hledger*/hledger*m4.md). We want all of these updated in lock step with code/tooling changes.
Other (“soft”) docs are needed, but these have a more relaxed process, schedule, and scope (eg bookkeeping advice). They occasionally generate a lot of noise in the commit log, and I think it’s a good to keep that out of the code history. The website (home and other pages, site design, site infrastructure) generates similar commit storms and is somewhat independent of code, so it goes in the soft docs repo too.
These are my thoughts, but I have an open mind if you see a better way.
me (Simon Michael (sm) change)
10/27/18 Still plenty of time to discuss and reconsider, but see also https://github.com/simonmichael/hledger/issues/920 https://github.com/simonmichael/hledger/issues/921I’ll probably make a start on the first one (consolidating dev docs in main repo).
ERRORS
hledger produces a variety of error messages in different situations. They are intended first for humans and secondly for parsing by tools like flycheck-hledger. Here we collect examples of these errors, which are listed below for reference, and also tested as part of our test suite. This document is the README in the hledger/test/errors/ directory, and also published as Developer docs > ERRORS on hledger.org.
Each error has
- a name (similar to hledger check names when applicable)
- an executable script in this directory which reproduces the error (like
balanced.j) - which usually can also be used as a data file for manual testing (
hledger -f balanced.j check) - sometimes supplementary data files (like
csvdateparse.csv.rules) - an autogenerated shelltestrunner test (like
balanced.test)
Procedures
To update this readme with the latest error messages, run make readme.
To regenerate the shelltestrunner tests, run make tests.
This is imperfect; some tests will require manual fixups (see also hledger2shelltest).
To test all of these error messages with hledger, run make test.
Requires shelltestrunner, the latest version is best.
To test these errors with flycheck-hledger,
customize flycheck-hledger-* in Emacs to enable all appropriate checks,
then open the data files to see how flycheck handles them.
C-c ! l opens a pane for easy viewing.
Error improvement
Work is ongoing to make our error messages more consistent and more useful (#1436, #1885, #1886..). This is a big project, and your help is welcome; every bit of progress counts, and this is a fast way to help users.
-
phase 1: update flycheck to detect journal errors of current hledger release (and keep a branch updated to detect errors of latest hledger master) -
phase 2: survey/document current journal errors & status -
phase 3: pick a new standard format -
phase 4: implement standard format for all -
phase 5: implement accurate lines for all -
phase 6: implement accurate columns for all [where possible; we currently do not save the position of every part of the transaction, so most errors do not report columns] -
phase 7: implement useful highlighted excerpts for all [we show imperfect but useful highlighted regions] -
phase 8: implement accurate flycheck region for all [flycheck-detected regions are imperfect but useful] -
phase 9: do likewise for timeclock errors -
phase 10: do likewise for timedot errors - phase 11: do likewise for csv errors
- phase 12: do likewise for other command line errors
-
phase 13: decide/add error ids/explanations/web pages ? not needed - phase 14: support Language Server Protocol & Visual Code
Standard error format
Here is our current preferred layout for error messages. It is similar to the parse error messages we get from megaparsec (since it’s easier to follow that than change it):
hledger: Error: FILE:LOCATION:
EXCERPT
EXPLANATION
Notes:
- line 1 includes “hledger” (dropping this would require some effort), the word “Error”, and the error position
- FILE is the file path
- LOCATION is
LINE[-ENDLINE][:COLUMN[-ENDCOLUMN]] - EXCERPT is a short visual snippet whenever possible, with the error region highlighted, line numbers, and colour when supported. This section must be easy for flycheck to ignore. (All lines begin with a space or a digit.)
- EXPLANATION briefly explains the problem, and suggests remedies if possible. It can be dynamic, showing context-sensitive info. (ShellCheck’s summaries are static.)
- this layout is based on megaparsec’s. For comparison, rustc puts summary on line 1 and location on line 2:
Error[ID]: SUMMARY at FILE:LOCATION EXCERPT [DETAILS] - try https://github.com/mesabloo/diagnose, https://hackage.haskell.org/package/errata, https://hackage.haskell.org/package/chapelure later ?
Limitations
Here are some current limitations of hledger’s error messages:
-
We report only one error at a time. You have to fix (or bypass) the current error to see any others.
-
We currently don’t save perfect position information when parsing, so we sometimes report only line number(s), without column number(s).
-
For the same reason, the excerpts we show in error messages are not the original data. Instead we show a synthetic rendering that is similar enough to be explanatory.
Error messages
Here is the current status as of hledger (see version below) and flycheck-hledger g1310cb518. Click error names to see an example. The table headings mean:
- std format - the error message follows our standard error format
- line - correct line numbers are reported
- column - useful column numbers are reported
- excerpt - a useful excerpt is shown, ideally with the error highlighted (✓✓)
- flycheck - the current flycheck release (or a PR branch) recognises the error and highlights a useful region
| error name | std format | line | column | excerpt | flycheck |
|---|---|---|---|---|---|
| accounts | ✓ | ✓ | ✓ | ✓✓ | ✓ |
| assertions | ✓ | ✓ | ✓ | ✓✓ | ✓ |
| autobalanced | ✓ | ✓ | - | ✓ | ✓ |
| balanced | ✓ | ✓ | - | ✓ | ✓ |
| commodities | ✓ | ✓ | ✓ | ✓✓ | ✓ |
| ordereddates | ✓ | ✓ | ✓ | ✓✓ | ✓ |
| parseable | ✓ | ✓ | ✓ | ✓✓ | ✓ |
| parseable-dates | ✓ | ✓ | ✓ | ✓✓ | ✓ |
| parseable-regexps | ✓ | ✓ | ✓ | ✓✓ | ✓ |
| payees | ✓ | ✓ | ✓ | ✓✓ | ✓ |
| recentassertions | ✓ | ✓ | ✓ | ✓✓ | ✓ |
| uniqueleafnames | ✓ | ✓ | ✓ | ✓✓ | ✓ |
| tcclockouttime | ✓ | ✓ | ✓ | ✓✓ | |
| tcorderedactions | ✓ | ✓ | ✓ | ✓✓ | |
| csvamountonenonzero | semi-std | ||||
| csvamountparse | |||||
| csvbalanceparse | |||||
| csvbalancetypeparse | |||||
| csvdateformat | |||||
| csvdateparse | |||||
| csvdaterule | |||||
| csvdecimalmarkparse | |||||
| csvifblocknonempty | ✓ | ✓ | ✓ | ||
| csviftablefieldnames | ✓ | ✓ | ✓✓ | ||
| csviftablenonempty | ✓ | ✓ | ✓ | ||
| csviftablevaluecount | ✓ | ✓ | ✓ | ||
| csvnoinclude | ✓ | ✓ | ✓ | ||
| csvskipvalue | |||||
| csvstatusparse | ✓ | ||||
| csvstdinrules | |||||
| csvtwofields | |||||
| csvstdinrules |
hledger 1.50.99-g9031612c3-20251117 error messages:
accounts
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./accounts.j:4:
| 2022-01-01
4 | (ß) 1
| ^
Strict account checking is enabled, and
account "ß" has not been declared.
Consider adding an account directive. Examples:
account ß
assertions
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./assertions.j:4:8:
| 2022-01-01
4 | a 0 = 1
| ^^^
Balance assertion failed in a
In commodity "" at this point, excluding subaccounts, ignoring costs,
the asserted balance is: 1
but the calculated balance is: 0
(difference: 1)
To troubleshoot, check this account's running balance with assertions disabled, eg:
hledger reg -I 'a$' cur:
autobalanced
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./autobalanced.j:3-4:
3 | 2022-01-01
| a 1
This transaction is unbalanced.
The real postings' sum should be 0 but is: 1
balanced
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./balanced.j:5-7:
5 | 2022-01-01
| a 1 A
| b -1 B
This multi-commodity transaction is unbalanced.
Automatic commodity conversion is not enabled.
The real postings' sum should be 0 but is: 1 A, -1 B
Consider adjusting this entry's amounts, adding missing postings,
or recording conversion price(s) with @, @@ or equity postings.
commodities
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./commodities.j:6:
| 2022-01-01
6 | (a) A 1
| ^^^
Strict commodity checking is enabled, and
commodity "A" has not been declared.
Consider adding a commodity directive. Examples:
commodity A1000.00
commodity 1.000,00 A
ordereddates
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./ordereddates.j:10:
7 | 2022-01-02 p
| (a) 1
10 | 2022-01-01 p
| ^^^^^^^^^^
| (a) 1
Ordered dates checking is enabled, and this transaction's
date (2022-01-01) is out of order with the previous transaction.
Consider moving this entry into date order, or adjusting its date.
parseable-dates
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./parseable-dates.j:3:1:
|
3 | 2022/1/32
| ^^^^^^^^^
This is not a valid date, please fix it.
parseable-regexps
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./parseable-regexps.j:3:8:
|
3 | alias /(/ = a
| ^
This regular expression is invalid or unsupported, please correct it:
(
parseable
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./parseable.j:3:2:
|
3 | 1
| ^
unexpected newline
expecting date separator or digit
payees
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./payees.j:6:
6 | 2022-01-01 p
| ^
| (a) A 1
Strict payee checking is enabled, and
payee "p" has not been declared.
Consider adding a payee directive. Examples:
payee p
recentassertions
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./recentassertions.j:18:
| 2022-01-09 bad1
18 | a 0
| ^
The recentassertions check is enabled, so accounts with balance assertions
must have a recent one, not more than 7 days older than their latest posting.
In account: a
the last assertion was on 2022-01-01, 8 days before this latest posting.
Consider adding a new balance assertion to the above posting. Eg:
a 0 = BALANCE
uniqueleafnames
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./uniqueleafnames.j:12:
| 2022-01-01 p
9 | (a:c) 1
...
| 2022-01-01 p
12 | (b:c) 1
| ^
Checking for unique account leaf names is enabled, and
account leaf name "c" is not unique.
It appears in these account names, which are used in 2 places:
a:c
b:c
Consider changing these account names so their last parts are different.
tcclockouttime
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./tcclockouttime.timeclock:4:1:
4 | i 2022-01-01 00:01:00 a
/Users/simon/src/hledger/hledger/test/errors/./tcclockouttime.timeclock:5:1:
5 | o 2022-01-01 00:00:00
:
This clockout is earlier than the clockin.
tcorderedactions
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./tcorderedactions.timeclock:8:1:
8 | i 2022-01-01 00:01:00 a
overlaps with session beginning at:
/Users/simon/src/hledger/hledger/test/errors/./tcorderedactions.timeclock:7:1:
7 | i 2022-01-01 00:00:00 a
Overlapping sessions with the same account name are not supported.
csvamountonenonzero
hledger: Error: in CSV rules:
While processing record: 2022-01-03,1,2
while calculating amount for posting 1
rule "amount-in %2" assigned value "1"
rule "amount-out %3" assigned value "2"
Multiple non-zero amounts were assigned for an amount field.
Please ensure just one non-zero amount is assigned, perhaps with an if rule.
See also: https://hledger.org/hledger.html#setting-amounts
(hledger manual -> CSV format -> Tips -> Setting amounts)
csvamountparse
hledger: Error: could not parse "badamount" as an amount
record: 2022-01-03,badamount
the amount rule is: %2
the date rule is: %1
the parse error is: 1:10:
|
1 | badamount
| ^
unexpected end of input
expecting '+', '-', or number
you may need to change your amount*, balance*, or currency* rules, or add or change your skip rule
csvbalanceparse
hledger: Error: could not parse "badbalance" as balance1 amount
record: 2022-01-03,badbalance
the balance rule is: %2
the date rule is: %1
the parse error is: 1:11:
|
1 | badbalance
| ^
unexpected end of input
expecting '+', '-', or number
csvbalancetypeparse
hledger: Error: balance-type "badtype" is invalid. Use =, ==, =* or ==*.
record: 2022-01-01,1
the balance rule is: %2
the date rule is: %1
csvdateformat
hledger: Error: could not parse "a" as a date using date format "YYYY/M/D", "YYYY-M-D" or "YYYY.M.D"
record: a,b
the date rule is: %1
the date-format is: unspecified
you may need to change your date rule, add a date-format rule, or change your skip rule
for m/d/y or d/m/y dates, use date-format %-m/%-d/%Y or date-format %-d/%-m/%Y
csvdateparse
hledger: Error: could not parse "baddate" as a date using date format "%Y-%m-%d"
record: baddate,b
the date rule is: %1
the date-format is: %Y-%m-%d
you may need to change your date rule, change your date-format rule, or change your skip rule
for m/d/y or d/m/y dates, use date-format %-m/%-d/%Y or date-format %-d/%-m/%Y
csvdaterule
hledger: Error: offset=0:
Please specify (at top level) the date field. Eg: date %1
csvdecimalmarkparse
hledger: Error: decimal-mark's argument should be "." or "," (not "badmark")
csvifblocknonempty
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./csvifblocknonempty.csv.rules:2:1:
|
2 | if foo
| ^
start of conditional block found, but no assignment rules afterward
(assignment rules in a conditional block should be indented)
csviftablefieldnames
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./csviftablefieldnames.csv.rules:2:9:
|
2 | if,date,nosuchfield,description
| ^^^^^^^^^^^^
unexpected "nosuchfield,"
expecting "account1", "account10", "account11", "account12", "account13", "account14", "account15", "account16", "account17", "account18", "account19", "account2", "account20", "account21", "account22", "account23", "account24", "account25", "account26", "account27", "account28", "account29", "account3", "account30", "account31", "account32", "account33", "account34", "account35", "account36", "account37", "account38", "account39", "account4", "account40", "account41", "account42", "account43", "account44", "account45", "account46", "account47", "account48", "account49", "account5", "account50", "account51", "account52", "account53", "account54", "account55", "account56", "account57", "account58", "account59", "account6", "account60", "account61", "account62", "account63", "account64", "account65", "account66", "account67", "account68", "account69", "account7", "account70", "account71", "account72", "account73", "account74", "account75", "account76", "account77", "account78", "account79", "account8", "account80", "account81", "account82", "account83", "account84", "account85", "account86", "account87", "account88", "account89", "account9", "account90", "account91", "account92", "account93", "account94", "account95", "account96", "account97", "account98", "account99", "amount", "amount-in", "amount-out", "amount1", "amount1-in", "amount1-out", "amount10", "amount10-in", "amount10-out", "amount11", "amount11-in", "amount11-out", "amount12", "amount12-in", "amount12-out", "amount13", "amount13-in", "amount13-out", "amount14", "amount14-in", "amount14-out", "amount15", "amount15-in", "amount15-out", "amount16", "amount16-in", "amount16-out", "amount17", "amount17-in", "amount17-out", "amount18", "amount18-in", "amount18-out", "amount19", "amount19-in", "amount19-out", "amount2", "amount2-in", "amount2-out", "amount20", "amount20-in", "amount20-out", "amount21", "amount21-in", "amount21-out", "amount22", "amount22-in", "amount22-out", "amount23", "amount23-in", "amount23-out", "amount24", "amount24-in", "amount24-out", "amount25", "amount25-in", "amount25-out", "amount26", "amount26-in", "amount26-out", "amount27", "amount27-in", "amount27-out", "amount28", "amount28-in", "amount28-out", "amount29", "amount29-in", "amount29-out", "amount3", "amount3-in", "amount3-out", "amount30", "amount30-in", "amount30-out", "amount31", "amount31-in", "amount31-out", "amount32", "amount32-in", "amount32-out", "amount33", "amount33-in", "amount33-out", "amount34", "amount34-in", "amount34-out", "amount35", "amount35-in", "amount35-out", "amount36", "amount36-in", "amount36-out", "amount37", "amount37-in", "amount37-out", "amount38", "amount38-in", "amount38-out", "amount39", "amount39-in", "amount39-out", "amount4", "amount4-in", "amount4-out", "amount40", "amount40-in", "amount40-out", "amount41", "amount41-in", "amount41-out", "amount42", "amount42-in", "amount42-out", "amount43", "amount43-in", "amount43-out", "amount44", "amount44-in", "amount44-out", "amount45", "amount45-in", "amount45-out", "amount46", "amount46-in", "amount46-out", "amount47", "amount47-in", "amount47-out", "amount48", "amount48-in", "amount48-out", "amount49", "amount49-in", "amount49-out", "amount5", "amount5-in", "amount5-out", "amount50", "amount50-in", "amount50-out", "amount51", "amount51-in", "amount51-out", "amount52", "amount52-in", "amount52-out", "amount53", "amount53-in", "amount53-out", "amount54", "amount54-in", "amount54-out", "amount55", "amount55-in", "amount55-out", "amount56", "amount56-in", "amount56-out", "amount57", "amount57-in", "amount57-out", "amount58", "amount58-in", "amount58-out", "amount59", "amount59-in", "amount59-out", "amount6", "amount6-in", "amount6-out", "amount60", "amount60-in", "amount60-out", "amount61", "amount61-in", "amount61-out", "amount62", "amount62-in", "amount62-out", "amount63", "amount63-in", "amount63-out", "amount64", "amount64-in", "amount64-out", "amount65", "amount65-in", "amount65-out", "amount66", "amount66-in", "amount66-out", "amount67", "amount67-in", "amount67-out", "amount68", "amount68-in", "amount68-out", "amount69", "amount69-in", "amount69-out", "amount7", "amount7-in", "amount7-out", "amount70", "amount70-in", "amount70-out", "amount71", "amount71-in", "amount71-out", "amount72", "amount72-in", "amount72-out", "amount73", "amount73-in", "amount73-out", "amount74", "amount74-in", "amount74-out", "amount75", "amount75-in", "amount75-out", "amount76", "amount76-in", "amount76-out", "amount77", "amount77-in", "amount77-out", "amount78", "amount78-in", "amount78-out", "amount79", "amount79-in", "amount79-out", "amount8", "amount8-in", "amount8-out", "amount80", "amount80-in", "amount80-out", "amount81", "amount81-in", "amount81-out", "amount82", "amount82-in", "amount82-out", "amount83", "amount83-in", "amount83-out", "amount84", "amount84-in", "amount84-out", "amount85", "amount85-in", "amount85-out", "amount86", "amount86-in", "amount86-out", "amount87", "amount87-in", "amount87-out", "amount88", "amount88-in", "amount88-out", "amount89", "amount89-in", "amount89-out", "amount9", "amount9-in", "amount9-out", "amount90", "amount90-in", "amount90-out", "amount91", "amount91-in", "amount91-out", "amount92", "amount92-in", "amount92-out", "amount93", "amount93-in", "amount93-out", "amount94", "amount94-in", "amount94-out", "amount95", "amount95-in", "amount95-out", "amount96", "amount96-in", "amount96-out", "amount97", "amount97-in", "amount97-out", "amount98", "amount98-in", "amount98-out", "amount99", "amount99-in", "amount99-out", "balance", "balance1", "balance10", "balance11", "balance12", "balance13", "balance14", "balance15", "balance16", "balance17", "balance18", "balance19", "balance2", "balance20", "balance21", "balance22", "balance23", "balance24", "balance25", "balance26", "balance27", "balance28", "balance29", "balance3", "balance30", "balance31", "balance32", "balance33", "balance34", "balance35", "balance36", "balance37", "balance38", "balance39", "balance4", "balance40", "balance41", "balance42", "balance43", "balance44", "balance45", "balance46", "balance47", "balance48", "balance49", "balance5", "balance50", "balance51", "balance52", "balance53", "balance54", "balance55", "balance56", "balance57", "balance58", "balance59", "balance6", "balance60", "balance61", "balance62", "balance63", "balance64", "balance65", "balance66", "balance67", "balance68", "balance69", "balance7", "balance70", "balance71", "balance72", "balance73", "balance74", "balance75", "balance76", "balance77", "balance78", "balance79", "balance8", "balance80", "balance81", "balance82", "balance83", "balance84", "balance85", "balance86", "balance87", "balance88", "balance89", "balance9", "balance90", "balance91", "balance92", "balance93", "balance94", "balance95", "balance96", "balance97", "balance98", "balance99", "code", "comment", "comment1", "comment10", "comment11", "comment12", "comment13", "comment14", "comment15", "comment16", "comment17", "comment18", "comment19", "comment2", "comment20", "comment21", "comment22", "comment23", "comment24", "comment25", "comment26", "comment27", "comment28", "comment29", "comment3", "comment30", "comment31", "comment32", "comment33", "comment34", "comment35", "comment36", "comment37", "comment38", "comment39", "comment4", "comment40", "comment41", "comment42", "comment43", "comment44", "comment45", "comment46", "comment47", "comment48", "comment49", "comment5", "comment50", "comment51", "comment52", "comment53", "comment54", "comment55", "comment56", "comment57", "comment58", "comment59", "comment6", "comment60", "comment61", "comment62", "comment63", "comment64", "comment65", "comment66", "comment67", "comment68", "comment69", "comment7", "comment70", "comment71", "comment72", "comment73", "comment74", "comment75", "comment76", "comment77", "comment78", "comment79", "comment8", "comment80", "comment81", "comment82", "comment83", "comment84", "comment85", "comment86", "comment87", "comment88", "comment89", "comment9", "comment90", "comment91", "comment92", "comment93", "comment94", "comment95", "comment96", "comment97", "comment98", "comment99", "currency", "currency1", "currency10", "currency11", "currency12", "currency13", "currency14", "currency15", "currency16", "currency17", "currency18", "currency19", "currency2", "currency20", "currency21", "currency22", "currency23", "currency24", "currency25", "currency26", "currency27", "currency28", "currency29", "currency3", "currency30", "currency31", "currency32", "currency33", "currency34", "currency35", "currency36", "currency37", "currency38", "currency39", "currency4", "currency40", "currency41", "currency42", "currency43", "currency44", "currency45", "currency46", "currency47", "currency48", "currency49", "currency5", "currency50", "currency51", "currency52", "currency53", "currency54", "currency55", "currency56", "currency57", "currency58", "currency59", "currency6", "currency60", "currency61", "currency62", "currency63", "currency64", "currency65", "currency66", "currency67", "currency68", "currency69", "currency7", "currency70", "currency71", "currency72", "currency73", "currency74", "currency75", "currency76", "currency77", "currency78", "currency79", "currency8", "currency80", "currency81", "currency82", "currency83", "currency84", "currency85", "currency86", "currency87", "currency88", "currency89", "currency9", "currency90", "currency91", "currency92", "currency93", "currency94", "currency95", "currency96", "currency97", "currency98", "currency99", "date", "date2", "description", "end", "skip", or "status"
csviftablenonempty
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./csviftablenonempty.csv.rules:2:1:
|
2 | if,date,description,comment
| ^
start of conditional table found, but no assignment rules afterward
csviftablevaluecount
hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./csviftablevaluecount.csv.rules:4:1:
|
4 | one,val1
| ^
line of conditional table should have 2 values, but this one has only 1
csvnoinclude
hledger: Error: in file included from /Users/simon/src/hledger/hledger/test/errors/./csvnoinclude.j,
/Users/simon/src/hledger/hledger/test/errors/csvnoinclude.csv:1:1:
|
1 | <empty line>
| ^
sorry, CSV files can't be included yet
csvskipvalue
hledger: Error: could not parse skip value: badval
csvstatusparse
hledger: Error: could not parse status value "badstatus" (should be *, ! or empty)
the parse error is: 1:1:
|
1 | badstatus
| ^
unexpected 'b'
expecting '!', '*', or end of input
csvstdinrules
hledger: Error: please use --rules when reading CSV from stdin
csvtwofields
hledger: Error: CSV record ["b"] has less than two fields
csvstdinrules
hledger: Error: please use --rules when reading CSV from stdin
EXAMPLES
Collected examples
Many example input files in journal and other formats can be found
in examples/ in the hledger repo.
Sample journals
Synthetic data files like examples/100x100x10.journal are useful for benchmarks and testing.
The numbers describe the number of transactions, number of accounts, and maximum account depth respectively.
They are generated by tools/generatejournal.hs.
They should get built automatically as needed, if not you can use make samplejournals:
$ just samplejournals
ghc tools/generatejournal.hs
[1 of 1] Compiling Main ( tools/generatejournal.hs, tools/generatejournal.o )
Linking tools/generatejournal ...
tools/generatejournal 100 100 10 >examples/100x100x10.journal
tools/generatejournal 1000 1000 10 >examples/1000x1000x10.journal
tools/generatejournal 1000 10000 10 >examples/1000x10000x10.journal
tools/generatejournal 10000 1000 10 >examples/10000x1000x10.journal
tools/generatejournal 10000 10000 10 >examples/10000x10000x10.journal
tools/generatejournal 100000 1000 10 >examples/100000x1000x10.journal
tools/generatejournal 3 5 5 >examples/ascii.journal
tools/generatejournal 3 5 5 --chinese >examples/chinese.journal
tools/generatejournal 3 5 5 --mixed >examples/mixed.journal
Files
Some views and explanations of files in the hledger project, as of 2022-12. (Needs update..)
hledger working copy
A full working copy of the official hledger repos is best laid out like this (manually; we currently don’t use git submodules):
src/hledger/ - git clone https://github.com/simonmichael/hledger; cd hledger site/ - git clone https://github.com/simonmichael/hledger_site site finance/ - git clone https://github.com/simonmichael/hledger_finance finance
You don’t need to clone all of these repos unless you are working in all of those areas.
The next two listings show the directories in the main and site repos:
main repo directories
The main repo contains the hledger-lib, hledger, hledger-ui, and hledger-web haskell packages, the hledger-install script, a collection of example data, some documentation and other support files.
src/hledger/
.github/
ISSUE_TEMPLATE/
workflows/
old/
.sandstorm/
bin/
checks/
doc/
haskellerz/
hcar/
mockups/
profs/
docker-static-arm32v7/
docker/
examples/
budgeting/
csv/
investing/
invoicing/
invoice-script/
makefile/
reports/
systemd/
templates/
basic/
hledger-install/
hledger-lib/
Hledger/
Data/
JournalChecks/
Read/
Reports/
Utils/
Text/
Megaparsec/
Tabular/
other/
ledger-parse/
Ledger/
Parser/
test/
hledger-ui/
Hledger/
UI/
test/
hledger-web/
Hledger/
Web/
Handler/
Settings/
Widget/
app/
config/
deploy/
static/
css/
fonts/
js/
templates/
test/
hledger/
Hledger/
Cli/
Commands/
app/
bench/
embeddedfiles/
shell-completion/
test/
addons/
balance/
cli/
errors/
i18n/
import/
journal/
account-display-order/
1/
print/
register/
tools/
site repo directories
The site repo contains the website infrastructure, versioned snapshots of the user manuals, home page, cookbook and other docs.
src/hledger/site/
css/
js/
src/
1.0/
1.1/
1.10/
1.11/
1.12/
1.13/
1.14/
1.15/
1.16/
1.17/
1.18/
1.19/
1.2/
1.20/
1.21/
1.22/
1.23/
1.24/
1.25/
1.26/
1.27/
1.28/
1.3/
1.4/
1.5/
1.9/
dev/
fonts/
highslide/
graphics/
outlines/
images/
hledger-ui/
hledger-web/
normal/
small/
theme/
css/
finance repo
The finance repo contains transaction journals and financial reports.
src/hledger/finance/ Makefile README.md oc.accounts oc.csv oc.csv.rules oc.journal
hledger user scripts
Scripts for users are in bin/:
src/hledger/bin/ README.md _hledger-chart.hs bashrc compile.sh csv.mk hledger-addon-example.hs hledger-balance-as-budget.hs hledger-check-fancyassertions.hs hledger-check-postable.hs hledger-check-tagfiles.cabal.hs hledger-check-tagfiles.hs hledger-combine-balances.hs hledger-git hledger-move.hs hledger-pijul hledger-print-location.hs hledger-simplebal hledger-smooth.hs hledger-swap-dates.hs paypaljson paypaljson2csv scripts.test watchaccounts
hledger developer tools
Scripts used by developers and maintainers tend to be in tools/:
src/hledger/tools/ README changelog.hs commitlint criterionbench.hs dayssincetag.hs docshelltest.hs generatejournal.hs generatetimeclock.hs gtree hackageupload listbydeps.hs pandoc-dedent-code-blocks.lua pandoc-demote-headers.lua pandoc-drop-html-blocks.lua pandoc-drop-html-inlines.lua pandoc-drop-links.lua pandoc-drop-toc.lua pandoc-toc.lua pandoc-wiki-links.lua progressionbench.hs regressiontest.py release runhledgercov simplifyprof.hs trhsx
Sh/makefiles
Many developer tasks are automated via Justfile or Shake.hs also.
Run just or ./Shake.hs to see help.
$ gtree '(^|/)((bsd)?m|sh)ake|justfile'
src/hledger/
Justfile
Makefile
Makefile.helpsys
Shake.hs
bin/
justfile
doc/
haskellerz/
Makefile
examples/
csv/
Makefile
invoicing/
pandoc-make-invoice/
Makefile
reports/
Makefile
hledger/
shell-completion/
BSDmakefile
Makefile
test/
errors/
Makefile
YAML files
Developer configuration often happens in .yaml or .yml files.
src/hledger/
.hlint.yaml
azure-pipelines.yml
hie-other.yaml
stack.yaml
stack8.10.yaml
stack9.0.yaml
stack9.4.yaml
.github/
FUNDING.yml
ISSUE_TEMPLATE/
config.yml
workflows/
binaries-linux-arm32v7-static.yml
binaries-linux-x64-static.yml
binaries-mac-x64.yml
binaries-windows-x64.yml
test-linux-x64.yml
old/
release.yml
hledger-lib/
package.yaml
hledger-ui/
package.yaml
hledger-web/
package.yaml
config/
keter.yaml
settings.yml
hledger/
package.yaml
Core docs
Core documentation which should stay closely synced with hledger’s implementation (changelogs, user manuals, developer docs) is kept in the main repo.
-
Many directories have a README.md explaining their purpose and content.
-
Each hledger package, and the project itself, has a CHANGES.md changelog file.
-
hledger/hledger.m4.md, hledger-ui/hledger-ui.m4.md, hledger-web/hledger-web.m4.md are the user manuals, which get rendered as html, info, man and plain text. They are processed first with m4 for extra flexibility.
-
The hledger manual imports the subcommand docs from hledger/Hledger/Cli/Commands/*.md.
-
doc/ contains other developer docs.
src/hledger/
CHANGES.md
README.md
.github/
pull_request_template.md
ISSUE_TEMPLATE/
a-bug.md
a-wish.md
.sandstorm/
README.md
changelog.md
description.md
bin/
README.md
doc/
ACHIEVEMENTS.md
BENCHMARKS.md
CHANGELOGS.md
CODE.md
COMMITS.md
CONTRIBUTING.md
CREDITS.md
DOCS.md
EXAMPLES.md
FILES.md
FINANCE.md
ISSUES.md
LINKS.md
MAKE.md
PULLREQUESTS.md
RELEASING.md
REPOS.md
SHAKE.md
TESTS.md
VERSIONNUMBERS.md
DEVWORKFLOWS.md
github-release-doc.tmpl.md
haskellerz/
haskellerz.md
hcar/
HCAR-hledger-201611.md
docker/
README.md
examples/
invoicing/
README.md
invoice-script/
README.md
abinvoice.tmpl.md
makefile/
202001ab.md
README.md
systemd/
hledger-web.service
hledger.nginx
readme.md
templates/
README.md
hledger-install/
README.md
hledger-lib/
CHANGES.md
README.md
hledger-ui/
CHANGES.md
README.md
hledger-ui.m4.md
test/
uitest.md
hledger-web/
CHANGES.md
README.md
hledger-web.m4.md
hledger/
CHANGES.md
README.md
hledger.m4.md
Hledger/
Cli/
Commands/
Accounts.md
Activity.md
Add.md
Aregister.md
Balance.md
Balancesheet.md
Balancesheetequity.md
Cashflow.md
Check.md
Close.md
Codes.md
Commodities.md
Descriptions.md
Diff.md
Files.md
Help.md
Import.md
Incomestatement.md
Notes.md
Payees.md
Prices.md
Print.md
README.md
Register.md
Rewrite.md
Repl.md
Roi.md
Run.md
Stats.md
Tags.md
Test.md
shell-completion/
README.md
test/
README.md
errors/
README.md
Site config files
These help configure the website.
-
book.toml is the main config file for mdbook.
-
src/SUMMARY.md defines the site’s pages and which ones appear in the sidebar (except for old manual versions; those are rendered separately).
src/hledger/site/
Makefile
book.toml
css/
site.css
js/
bootstrap.min.js
jquery-1.11.0.min.js
site.js
src/
SUMMARY.md
highslide/
highslide-ie6.css
highslide.css
highslide.js
theme/
book.js
favicon.png
favicon.svg
highlight.css
highlight.js
index.hbs
css/
chrome.css
general.css
print.css
variables.css
Finance
This is the home for the hledger project’s finances. (Shortcut: hledger.org/finance)
Reports
We log the hledger project’s financial transactions and generate reports in the hledger_finance repo. There you can review our financial reports.
Money and the hledger project
Classic gift-economy FOSS communities are a beautiful and precious thing. Adding money, or adding money in the wrong way, can change or damage them. Yet, from the beginning I intended this project to be accountable, solvent and self-funding - ideally, not a money pit. And we need to be exploring new ways to integrate FOSS and money. And in many ways this project is ideal for that. So money is part of the hledger project, we welcome it, and we experiment with it, cautiously, to see what works. The CFO is SM; help is welcome.
You can Sponsor hledger to support and grow this project.
You can earn Regression bounties by reporting or fixing regression bugs.
Issue bounties
More generally, bounties are sometimes offered to encourage completion of a bug fix, or a new feature.
To earn bounties, try to resolve issues with the bounty label.
Here are the issue bounties.
Anyone can offer bounties. There are two kinds:
Honour bounties
You or your organisation can offer a bounty simply by saying so on the issue.
Progress and payment are discussed there and handled on the honour system.
The Bounty Manager is SM.
Open Collective bounties
Or the hledger project may offer bounties for specific issues, from our Open Collective fund.
Feel free to suggest issues which might benefit from this.
When you complete an OC bounty, submit an expense reimbursement request to hledger’s open collective,
similar to claiming a regression bounty.
Our bounty amounts have been relatively small so far, but they help us experiment with process and establish a principle of sustainability. When you are awarded a bounty, we encourage you to claim it. But if you prefer, you can donate it back to the project, or declare it as a new bounty on an issue of your choice.
Funded projects
We welcome proposals for new funded projects (since 2025).
Anyone can propose a project and an amount they’ll do it for,
and if approved, do the work, and as deliverables are met, we’ll pay you.
The hledger project’s cashflow is currently rather limited,
so we invite other organisations or individuals to help fund projects (or call for proposals).
Each proposal begins as an issue, which gets the funded-project label when approved.
Here are the funded projects.
Issues
The hledger project's issue tracker is on github.
Shortcut urls:
- https://issues.hledger.org - this page
- https://wishes.hledger.org - open WISHes in the github issue tracker
- https://bugs.hledger.org - open BUGs in the github issue tracker
- https://bugs.hledger.org/new - report a new issue
- https://hledger.org/regressions - how to claim regression bounties
Misc. issue queries:
- critical bugs, and major bugs affecting many users (open)
- major bugs affecting fewer users (open)
- blocker bugs affecting many users (open)
- bugs without an “affects” impact label
- bugs without an “annoyance” severity label
- issues of unknown type
- assigned open issues
- closed issues with needs-* labels
(Most older issues don’t have impact/severity labels, so those reports are recent-biased)
Open issues grouped by topic/type:
Labels
In the github issue tracker we use labels to categorise things like:
- whether an issue is a bug (red) or a wish (purple)
- bug impact - who is affected (light yellow)
- bug severity - how annoying is it (light pink)
- which subcomponents (tools, commands, input/output formats) are involved (light blue)
- which platforms are involved (blue)
- regressions (black)
- security issues (bright red)
- issues with bounties (bright green)
- PRs needing release (yellow)
- PR/issue blockers (grey)
- miscellaneous (white)
The subcomponent names are also used in commit message prefixes, in issue title prefixes, etc.
Some other loose conventions:
-
We sometimes prefix bug titles (especially regressions) with the hledger version(s) affected. This allows searches like new issues in 1.22 and regressions in 1.22
-
In the past we tracked some time estimates in brackets after the issue title. The format was something like
[ESTIMATEDTOTALTASKTIME|TIMESPENTSOFAR]. Eg:[2]two hours estimated, no time spent yet[..]half an hour estimated (a dot is ~a quarter hour, as in timedot format)[1d]one day estimated (a day is ~4 hours)[1w]one week estimated (a week is ~5 days or ~20 hours)[3|2]three hours estimated, about two hours spent so far[1|1w|2d]initially estimated as one hour, later estimated as one week, two days spent so far
Prioritising
https://lostgarden.home.blog/2008/05/20/improving-bug-triage-with-user-pain/ describes an interesting method of ranking issues by a single “User Pain” metric. Here’s the simplified version that we are using in the hledger issue tracker:
Two labels can be applied to bug reports,
each beginning with the letter a so as to appear near the front of label lists,
each with a level from 1 to 5:
Impact
Who may be affected by this bug ?
- affects1-noone: Affects almost no one.
- affects2-devs: Affects packagers or developers.
- affects3-few: Affects just a few users.
- affects4-many: Affects potentially a significant number of users.
- affects5-most: Affects most or all users.
Severity
To people affected, how serious is this bug ?
- annoyance1-trivial: Cleanliness/consistency/developer bug. Only perfectionists care.
- annoyance2-minor: Minor to moderate usability/doc bug, reasonably easy to avoid or tolerate.
- annoyance3-blocker: New user experience or installability bug. A potential user could fail to get started.
- annoyance4-major: Major usability/doc bug, or any regression or crash.
- annoyance5-critical: Any loss of user’s data, privacy, security, or trust.
User Pain
The bug’s User Pain score is Impact * Severity (1 to 25).
Then, practices like these are possible:
- Open bugs can be prioritised in order of User Pain.
- The team can set easy-to-understand quality bars. For example, they could say “In order to release, we must have no open bugs with more than 15 pain.”
- If there are no bugs left above the current quality bar, they can work on feature work.
- If a bug is found that will take more than a week to fix, it can be flagged as a ‘killer’ bug, for special treatment.
Reducing bugs and regressions
Some ideas in 2024-01:
- Maintain ratio of user-visible bugfixes to new features, eg above 10:1 (a new master merge test, human checked)
- A release cycle with no new features
- Alternate bugfix and feature release cycles
- Set bug count targets
- Label all issues for impact/severity/user pain; set max user pain targets
- Gate releases on user pain targets or other bug metrics
- Document and follow more disciplined bug triage/fixing methods
- Identify every new bug early as a regression/non-regression
- Prioritise rapid fixing and releasing for regressions / new bugs
- Cheaper, more frequent bugfix releases
- More intentional systematic tests ? Analyse for weak spots ?
- Property tests ?
- Internal cleanup, architectural improvements, more type safety ?
- Custom issue dashboards (HTMX on hledger.org ?)
- Public list / QA dashboard
- Grow a QA team
Older ideas
- Custodians for particular components/topics (“If you are interested in helping with a particular component for a while, please add yourself as a custodian in the Open Issues table. A custodian's job is to help manage the issues, rally the troops, and drive the open issue count towards zero. The more custodians, the better! By dividing up the work this way, we can scale and make forward progress.”)
Other
In 2017 we experimented with Github projects, in 2018 with Github milestones.
Long ago we collected some wishlist items in a trello board (trello.hledger.org).
Just, Make, Shake
After many years of relying on GNU Make for automating project tasks,
we now primarily use just instead.
just is better enough, and clean consolidated efficient project automation is so valuable, that this is worthwhile -
even though just requires installing and lacks Make’s file dependency tracking, for now.
Justfile in the main repo’s top directory is the starting point for scripts automating hledger project tasks.
Install and run just to list them. I suggest using a shorter command alias j. Eg:
$ alias j=just
$ j # list all scripts
$ j h test # list scripts mentioning "test"
Makefiles are still used in a number of subdirectories, like site/ and finance/.
Shake
Shake.hs in the main repo’s top directory complements the Justfile.
Tasks requiring file dependencies and/or more complex code, such as building documentation and the web site,
are usually scripted here, often with a corresponding alias in Justfile.
Eg:
$ ./Shake.hs # compile it for speed and git branch independence. Or: just Shake
$ ./Shake # list scripts
$ j site # runs the `just site` which calls `./Shake site` to do the work
$ j -n site # just --dry-run, show the commands that `just site` will run
tools
Additional helper scripts and tools are kept in the tools/ subdirectory.
MOCKUPS
Old mockups, draft docs and notes exploring possible future features. See also https://github.com/simonmichael/hledger/tree/master/doc/mockups
Ease of getting started
What could make getting started substantially easier ?
- Official CI-generated binaries for all major platforms
- Builtin access to docs in web format
Web docs
Provide the embedded user manuals as HTML also. Eg:
- hledger help –html # temporary static html files
- hledger help –web # serve from local hledger-web instance if installed
- hledger help –site # on hledger.org
- hledger-ui ? h/w/s # same as above
- hledger-web -> help # served from hledger-web
Config file
Name: hledger.conf (and possibly ~/.hledger.conf as well).
- easy to say and spell
- good highlighting support in editors
Format: toml/ini-ish format, but customised for our needs (if necessary).
Example:
# hledger.conf
[defaults]
# Set options/arguments to be always used with hledger commands.
# Each line is: HLEDGERCMD ARGS, or: hledger ARGS
hledger -f hledger.journal
bal -M --flat -b lastmonth
ui --watch
web -V
help --html
[commands]
# Define aliases for custom hledger commands.
# Each line is: CMDALIAS = HLEDGERCMD ARGS
assets = bal -M ^assets\b
liab = bal -M ^liabilities\b
# Or use colon, like make ?
bs2: bs --no-total date:thisyear
# Or just whitespace, like hledger csv rules ?
smui ui ^sm\b
# Allow arbitrary shell commands ?
2019: hledger -f 2019.journal
jstatus: git status -sb -- *.journal
# Allow multi-command shell scripts, with optional help string ?
bsis:
"Show monthly balance sheet and income statement"
hledger bs -M
echo
hledger is -M
echo
Loaded:
- at startup and ideally:
- hledger-web: on each page load if changed, like journals
- hledger-ui –watch: on change, like journals
Location:
Search a number of locations in order. Values from multiple files are combined, with later files taking precedence.
User config file: should it be “modern” ~/.config/hledger.conf or “old/simple” ~/.hledger.conf ? One or the other may be preferred/easier/more portable. If we support both, should it be one or the other, or both ?
Parent directory config files: we’d probably like to recognise config files in parent directories. How far up should we look - to the root dir ? to the user’s home dir ? and if not under the user’s home dir, don’t look up at all ? to the nearest VCS working directory root ?
This would be the simplest comprehensive scheme: use all of
- ~/.config/hledger.conf
- ~/.hledger.conf
- hledger.conf in all directories from / down to the current directory
Eg: running hledger in /home/simon/project/finance would combine any of the following which exist:
- ~/.config/hledger.conf
- ~/.hledger.conf
- /hledger.conf
- /home/hledger.conf
- /home/simon/hledger.conf
- /home/simon/project/hledger.conf
- /home/simon/project/finance/hledger.conf
Pull requests
Shortcut urls:
- http://prs.hledger.org
- http://draftprs.hledger.org
- http://readyprs.hledger.org
- http://assignedprs.hledger.org
- http://unassignedprs.hledger.org
Most contributed hledger code (and some of the project maintainer’s code) is submitted and reviewed via Github pull requests. Here are some tips for contributing PRs to hledger.
Code review is important
We aim to improve and sustain hledger’s quality and maintainability over the long term.
Many PRs, especially small ones, and even some big ones, can be merged quickly. We love merging PRs quickly.
Some bigger or more risky PRs can require substantial review, discussion, changes, or re-submission. Sometimes this is a bigger task than the coding. Much valuable design, quality control, and knowledge sharing happens at this time.
Some PRs ultimately get rejected, but their discussion and exploration can still be a useful contribution. We very much want to avoid wasted work, but it occasionally happens. Our process is evolving and imperfect. All of this is a normal part of software development.
We hope you’ll see it as a golden opportunity to collaborate with experts, share and receive knowledge, refine your design/documentation/code, and practice real-world development and communication skills. Patience and persistence pays.
The pull request
A PR should have a clear purpose, documented in its description. Mention any #ISSUENOs addressed.
Don’t tackle too much at once. Smaller/more focussed PRs can be reviewed quicker and accepted (or rejected) quicker.
Consider showing a draft of documentation first (more on this below).
When you are not ready for the PR to be merged, please make it a Draft PR. (Non-draft means “I think this is ready, you can merge any time”.) And use a normal PR title, ie don’t write “WIP:” or “Draft:” in the title.
The commit(s)
Commits should be easy to review. Ideally each commit is complete, and has a single clear purpose, which should be documented in the summary (and long description, if needed). #ISSUENOs can be mentioned in summary/description too when appropriate.
Within the above constraint, fewer, larger commits are preferred.
Keep in mind that commit messages are valuable documentation for future developers and troubleshooters. They are also the starting point for package changelogs and hledger release notes. High-quality commit messages makes the release process quicker, and the resulting docs better.
User-impacting commits should mention the user-visible changes, and be described in user-relevant language. Library-user-impacting commits, eg API changes, ideally will also be called out, and can described in more technical language. Commits affecting hledger internals are less important, but you may notice some adhoc conventions if you browse the history. In particular, you can optionally prefix the summary with short component codes (cf Issues) to facilitate history reading and changelog/release note production.
Rewrite and force-push your commits freely (rebase -i, push -f) to clean them up. Unless we decide to squash the PR into one commit, your commits will become part of hledger’s history “for all time”, so think about future developers trying to understand them, git bisect, etc.
Rebase your commits against latest master for easiest review. Especially if they start to conflict.
We like to use some conventions in commit messages when it makes sense. These aren’t mandatory, but appreciated:
- prepend a topic label prefix, eg
cli:orjournal:, for clarity and to help with changelog production - prepend a semicolon (
;) to indicate commits that- need not trigger a CI workflows, reducing wasteful carbon emissions
- and probably need not be mentioned in changelogs/release notes
The docs
PRs should include appropriate updates to reference documentation, unless otherwise agreed. Typically this means the manual source files (hledger*/hledger*.m4.md). It can also involve command line option names and descriptions, other –help output, hledger’s commands list, hledger-ui’s help dialog, hledger-web’s help dialog, etc. Sometimes it means the developer docs, at least the ones in the main repo (READMEs).
Reviewers can understand your PR more efficiently once proposed doc changes are provided, and may postpone it otherwise. We are happy to help with the docs if needed - just ask.
Updating rendered manuals (hledger.{1,info,txt,md,html}) is not required, and probably best avoided to reduce conflicts. Updating other docs such as tutorials, how-tos, examples, or screenshots is not required, though it’s welcome (may be in a different repo).
Documentation first
hledger follows documentation-driven design. It is in fact highly effective, and highly recommended, to write the new docs (help text/user manual/haddocks/developer README..) before writing any code. You can share a rough draft on IRC, on the mail list, in an issue comment, or in a “WIP” PR starting with just the proposed docs commit.
This is often the quickest road to getting something merged into hledger. hledger’s many parts interact in surprisingly complex ways. The documentation-driven working style lets us discuss, clarify and reach a good-enough consensus economically, after which coding/review/acceptance can go quicker.
Related ideas
Neil Mitchell’s Blog - The One PR Per Day Rule
hledger
Robust, intuitive plain text accounting
Welcome! This is a brief intro to hledger. For a more detailed version, see the home page: https://hledger.org
hledger is lightweight, cross platform, multi-currency, double-entry accounting software. It lets you track money, investments, cryptocurrencies, invoices, time, inventory and more, in a safe, future-proof plain text data format with full version control and privacy.
hledger aims to help both computer experts and regular folks gain clarity in their finances and time management. Though the UIs are basic, hledger can model any accounting situation and provide precise, clear reports. It is reliable, quick, and backed by the highly supportive Plain Text Accounting ecosystem. Using it is an excellent way to learn double entry accounting.
Compared to other PTA apps, hledger is actively maintained, with regular releases, and a strong focus on being easy to use and practical for everyday accounting.
More features:
- Installs easily on unix, mac or windows
- Complete, built-in documentation in multiple formats, beginner videos, tutorials etc.
- Multiple UIs: command-line, terminal, web, mobile, editors/IDEs
- Good at importing and exporting CSV; also outputs text/HTML/JSON/SQL
- A robust, general, well-specified multi-currency accounting engine
- Fast, analysing 25k transactions per second on a macbook air m1
- Accurate to 255 decimal places
- Supports your preferred account names, currencies, number formats
- Inspired by and partly compatible with Ledger CLI; interconvertible with Beancount
- Scriptable by CLI, HTTP or API, with plenty of examples
- Clean Haskell codebase, continually improved since 2007, with $100 regression bounties
- Free software licensed under GNU General Public License v3.0 or later.
Examples
I use hledger to:
- track income and spending, sometimes with budgets
- see time reports by day/week/month/project
- track reimbursables, invoices and payments
- predict cashflow and account balances
- get accurate numbers for tax filing
- research past events
Here’s an example of the journal file format:
2022-01-01 opening balances as of this date
assets:bank:checking $1000
assets:bank:savings $2000
assets:cash $100
liabilities:creditcard $-50
equity:opening/closing balances
2022-01-15 market
expenses:food $50
assets:cash $-50
2022-02-01 GOODWORKS CORP
assets:bank:checking $1000
income:salary $-1000
and some simple reports:
$ hledger bs
Balance Sheet 2022-02-15
|| 2022-02-15
========================++============
Assets ||
------------------------++------------
assets:bank:checking || $2000
assets:bank:savings || $2000
assets:cash || $50
------------------------++------------
|| $4050
========================++============
Liabilities ||
------------------------++------------
liabilities:creditcard || $50
------------------------++------------
|| $50
========================++============
Net: || $4000
$ hledger is --monthly
Income Statement 2022-01-01..2022-02-28
|| Jan Feb
===============++=============
Revenues ||
---------------++-------------
income:salary || 0 $1000
---------------++-------------
|| 0 $1000
===============++=============
Expenses ||
---------------++-------------
expenses:food || $50 0
---------------++-------------
|| $50 0
===============++=============
Net: || $-50 $1000
More examples and screenshots: https://hledger.org/#how-to-get-started
Funding
hledger is brought to you by Simon Michael, 140+ contributors, and the generous financial sponsors below.
After enjoying some personal or organisational success with hledger, you might want to become one of them, to help support this work. It’s easy! Please see https://hledger.org/sponsor.html for details.
Organisational sponsors
Individual sponsors
Regression bounty
Shortcut: regressions.hledger.org
A regression is “something that used to work, that broke”, or “an unintended not-good change” in a released version of hledger.
We don’t like regressions. We want to detect them quickly, repair them quickly, and reduce their frequency. You can help by finding them, or fixing them!
Regression finder bounty
We offer a $50 bounty for newly reported regression bugs (arising any time in the past, still affecting latest master). (original announcement)
Regression fixer bounty
We offer a separate $50 bounty for regression bug fixes landed in master. (If you caused the regression, there’s no bounty, though we do appreciate you reporting or fixing it.)
Is there a time limit ?
Yes - once the bounty is confirmed, please claim it within 7 days. If not, it will be automatically converted to a project donation at that time. This reduces wasteful admin work.
Should I claim the bounty ?
If you don’t want the bounty, you can choose to donate it back to the project treasury, by saying so or simply by not claiming it within the time limit. But consider claiming - it’s good for the project in several ways.
How to claim the bounty
- Discover a new regression and report it in the hledger bug tracker.
And/or, fix an open issue which has the
regressionlabel. - Wait for confirmation from the issue manager (SM).
- Send an expense reimbursement request to hledger’s open collective.
Note this might reveal your real name.
Here’s the procedure:
- at hledger’s open collective, click Submit Expense. (If you are logged in, this will be under the ACTIONS menu.)
- choose Invoice; choose a Payout method; click Next
- Expense title: Regression (finder / fixer / finder & fixer) bounty for #NNNN
- Description: (Found / Fixed) ISSUEURL, WORKDATE(S)
- Date: today’s date (you may need to click and select it to make it work)
- Amount: USD 50 (or 100 if you found and fixed). Or convert to another currency with today’s rate (and mention that rate in Description).
- Next, Submit Expense
- Wait for the finance manager (SM) to get the email notification and approve it. This should not take more than a day or two. (For fastest response, mention on the issue page or in chat that you’ve submitted.)
- Wait for Open Collective to pay it. This should happen within a week (payouts happen twice a week).
Regressions reported
We aim for zero regressions, but with a project of hledger’s flexibility, features, and level of funding, that’s not easy! Here are the regressions found since 2020.
| New regressions | Fixed in | Finder | Fixer |
|---|---|---|---|
| 1.19 2020-09-01 | — | — | — |
| #1568 | 1.22 | jolmg (N/A) | N/A |
| #1688 | 1.23 | Simon Michael (N/A) | N/A |
| #1698 | 1.23 | David Lowe | N/A |
| #1745 | 1.24 | Arne Schlüter | N/A |
| #1800 | 1.25 | Chuck Holmes | N/A |
| 1.20 2020-12-05 | — | — | — |
| #1439 | 1.20.3 | apauley (N/A) | N/A |
| #1468 | 1.21 | Simon Michael (N/A) | N/A |
| #1566 | 1.22 | benwebber (N/A) | N/A |
| 1.21 2021-03-10 | — | — | — |
| #1508 | 1.22 | edlanglois (N/A) | N/A |
| #1523 | 1.22 | Simon Michael (N/A) | N/A |
| #1526 | 1.22 | lestephane (N/A) | N/A |
| #1527 | 1.22 | lestephane (N/A) | N/A |
| #1656 | 1.23 | Stephen Morgan | N/A |
| #2284 | 1.41 | lakshayg | GLaknovik (donated) |
| #2302 | 1.42 | lowercasename | Simon Michael |
| 1.22 2021-07-03 | — | — | — |
| #1597 | 1.22.1 | Simon Michael | N/A |
| #1607 | 1.22.1 | Simon Michael | N/A |
| #1625 | 1.22.1 | Julian Klode | N/A |
| #1736 | 1.24 | Romain Gehrig | N/A |
| #1851 | 1.26 | Eric Langlois | N/A |
| 1.22.1 2021-08-02 | — | — | — |
| #1638 | 1.22.2 | Yann Büchau | N/A |
| #1642 | 1.22.2 | Simon Michael (N/A) | N/A |
| 1.23 2021-09-21 | — | — | — |
| #1933 | 1.32 | Simon Michael | N/A |
| #2071 | 1.33 | William Pierce | Simon Michael |
| 1.24 2021-12-01 | — | — | — |
| #1782 | 1.24.1 | Simon Michael (N/A) | N/A |
| 1.25 2022-03-04 | — | — | — |
| #2032 | 1.32 | Simon Michael | N/A |
| #2196 | 1.33.1 | Pranesh Prakash | Simon Michael, Bas van Dijk #2224 (donated) |
| 1.26 2022-06-04 | — | — | — |
| 1.27 2022-09-01 | — | — | — |
| #1932 | 1.27.1 | Andras Fabian | N/A |
| #2018 | 1.29.2 | Allan Odgaard | N/A |
| #2469 | Quả Cầu | Simon Michael (N/A) | |
| 1.28 2022-12-01 | — | — | — |
| 1.29 2023-03-11 | — | — | — |
| #2012 | 1.32 | Simon Michael (N/A) | N/A |
| #2020 | 1.32 | Pablo Mora | N/A |
| #2023 | 1.32 | Simon Michael | N/A |
| #2034 | 1.32 | Simon Michael (N/A) | N/A |
| #2045 | 1.32 | Pranesh Prakash | N/A |
| #2153 | 1.32.3 | markokocic (donated) | N/A |
| 1.30 2023-06-01 | — | — | — |
| #2072 | 1.32.3 | Simon Michael, usaAmch (#2137, donated), ipvych (#2150) | Simon Michael |
| 1.31 2023-09-03 | — | — | — |
| #2091 | 1.32 | Petr Slansky | N/A |
| #2115 | 1.32 | usaAmch (donated) | N/A |
| 1.32 2023-12-01 | — | — | — |
| #2125 | 1.32.1 | Simon Michael (N/A) | N/A |
| #2127 | 1.32.2 | Rajeev N | N/A |
| #2130 | 1.32.2 | Simon Michael (N/A) | N/A |
| #2134 | 1.32.2 | pepe_pecas (donated) | N/A |
| #2156 | 1.32.3 | ishmaelavila (donated) | Simon Michael |
| #2254 | 1.41 | akanshaG42 (donated) | Simon Michael |
| #2407 | 1.43.2 | Simon Michael (N/A) | Simon Michael (N/A) |
| 1.32.1 2023-12-07 | — | — | — |
| #2444 | 1.50 | Simon Michael (N/A) | Simon Michael (N/A) |
| 1.33 2024-04-18 | — | — | — |
| #2227 | 1.41 | Henning Thielemann | Henning Thielemann |
| 1.34 2024-06-01 | — | — | — |
| 1.40 2024-09-09 | — | — | — |
| #2225 | 1.41 | Bas van Dijk (donated) | Henning Thielemann |
| 1.41 2024-12-09 | — | — | — |
| 1.42 2025-03-07 | — | — | — |
| #2352 | 1.42.1 | Matt Gass | N/A |
| #2386 | 1.42.2 | Simon Michael (N/A) | N/A |
| #2389 | 1.42.2 | Simaex | Simon Michael |
| 1.43 2025-06-01 | — | — | — |
| #2396 | 1.43.1 | aragaer (donated) | Simon Michael |
| #2397 | 1.43.1 | Dmitry Astapov (donated) | Simon Michael |
| #2405 | 1.43.2 | Simon Michael (N/A) | Simon Michael (N/A) |
| #2406 | 1.43.2 | Wojciech Geisler | Simon Michael |
| #2417 | 1.50 | Frank Schmidt | Simon Michael |
| #2478 | 1.50.3 | dotlambda | Simon Michael |
| 1.50 2025-09-03 | — | — | — |
| #2452 | 1.50.1 | Samim Pezeshki | Stephen Morgan (N/A) |
| #2454 | 1.50.1 | Nic Mollel | Stephen Morgan (N/A) |
| #2465 | 1.50.2 | Paul Dest | Simon Michael (N/A) |
| #2479 | 1.50.3 | Simon Michael | Simon Michael |
| #2493 | 1.50.3 | Simon Michael (N/A) | Simon Michael (N/A) |
| #2498 | 1.50.4 | Peter Sagerson (donated) | Simon Michael (N/A) |
| #2512 | 1.51.2 | amano-kenji (donated) | Simon Michael (N/A) |
| #2564 | 1.52 | Juliano Solanho | Juliano Solanho |
| 1.50.2 2025-09-26 | — | — | — |
| #2495 | 1.50.3 | alix-aspersa | Simon Michael |
| 1.50.3 2025-11-18 | — | — | — |
| #2514 | 1.51.2 | Simon Michael (N/A) | Simon Michael (N/A) |
| 1.51 2025-12-05 | — | — | — |
| #2503 | 1.50.5, 1.51.1 | Simon Michael (N/A) | Simon Michael (N/A) |
| 1.51.2 2026-01-08 | — | — | — |
| #2553 | 1.52 | pierovera | Simon Michael (N/A) |
RELEASING
Notes for hledger release managers and maintainers.
Goals
2025
- Make releasing easier
- Improve automation
- Improve process docs
- Track releasing and release engineering time explicitly, per release
2024
- Make releasing easier
2023
- Make releasing eas
yier
2022
- Update/consolidate release process docs,
- Establish routine
monthlyrelease cadence, - Make releasing easy
hledger release types
hledger major releases happen each quarter, normally at the start of the third month (see past releases). Bugfix releases follow those when needed, usually soon after. Preview/nightly releases may happen any time.
| Major release A.B | Bugfix release A.B.C | Preview/Nightly release A.B.99 | |
|---|---|---|---|
| Contains: | New features, breaking changes | Only bug fixes | Early snapshot of the next major release |
| When: | Start of third month in quarter: Mar, Jun, Sep, Dec | When needed | Occasionally, as needed |
| Deliverables: | |||
| Changelogs | ✓ | ✓ | ✓ |
| Github release | ✓ | ✓ | ✓ |
| Binaries | ✓ | ✓ | ✓ |
| Hackage release | ✓ | ✓ | |
| Install page | ✓ | ✓ | |
| Regression bounties | ✓ | ✓ | |
| Release notes | ✓ | ✓ | |
| Manuals snapshot | ✓ | ||
| Announcements | ✓ |
hledger release artifacts / value chain
Higher things depend on lower things. Release readiness and the release process go from the bottom of this diagram to the top.
Release script
Short version, based on the checklist below. Last updated: 2025-12
- finish fixes/features/docs in master
- begin draft builds:
j ghbin oldest - in release branch, update code:
j relbranch VER; minor: cherry-pick new changes from master - update manuals and website manuals:
j manuals-site - major: update website scripts/redirects: update
site/Makefile,site/js/site.js,site/hledger.org.caddy - update changelogs:
j changelogs; edit;j changelogs-finalise - update relnotes:
j relnotes; edit; commit - update announcements: edit
doc/ANNOUNCE - update install docs: edit
doc/ghrelnotes,doc/ghnightlynotes.md,site/src/install.md - tag:
j reltags - make release builds:
j ghbin - in master, merge updates from release branch
- release to hackage:
j hackageupload - push to github:
j reltags-push VER, VER-branch, master, site repo - release to github: make new github latest release from VER tag;
j ghrel-notes;j ghbin-download ghrel-upload - major: activate website scripts/redirects:
j site-restart - announce to matrix, irc, mail list, mastodon, forum, pta.o
- major: update dev version, dev tag:
j devtag-push - update manuals:
j manuals - update changelogs:
j changelogs; edit
Release checklist
This is the guide for doing a hledger release. It corresponds to the diagram above, with more detail of waypoints, required artifacts, and related commands. It should be improved each time it is used. Last updated: 2025-11
-
0. general
- when browser (Safari) refuses to show new content, use another
-
1. product
- blocking defects resolved
- desired improvements landed and stabilised
- building and passing tests with current ghcs, deps, and stackage snapshots
- building and passing tests on all platforms / with all ghc versions
just ghbin(or push to githubbinaries[-*]branch)just oldest(or push to githuboldestbranch)
-
2. product docs and metadata
- release branch
- version strings (in **/.version, */.version.m4, */package.yaml)
- cabal files x 4 (hledger*/hledger*.cabal)
just relbranch VER,just cabalfilestest - options help texts up to date (in CliOptions.hs, UIOptions.hs, WebOptions.hs)
stack build./Shake cmddocs -c - embedded manuals x 3
- generaloptions macro (in doc/common.m4)
- tool specific options in manuals (hledger*/hledger*.m4.md > # Options)
- man page dates (*/.date.m4)
- man (hledger*/hledger*.1)
- info (hledger*/hledger*.info)
- text (hledger*/hledger*.txt)
./Shake mandates./Shake manuals -c
- embedded tldr pages synced with upstream (doc/tldr/*)
just tldr-diff - embedded asciinema demos (hledger/embeddedfiles/*.cast)
- shell completions (hledger/shell-completion/hledger-completion.bash)
just completions, commit any changes - changelogs x 5 (*/CHANGES.md)
just changelogs [-c]
add notable changes from site, finance repos to project changelog (major release only)
add issue links withmd-issue-refs, uniquify
add author github nicksjust changelogs-finalise
-
3. release docs and artifacts
- draft binaries building started
just ghbin - hledger.org html manuals x 3 (site/src/MAJORVER/*.md) (major release only)
just site-manuals-snapshot MAJORVERto create/update
updatesite/Makefile,site/js/site.js,site/hledger.org.caddy - release notes @ hledger.org (doc/relnotes.md)
just relnotes(XXX minor release: moves previous release’s summary, adds whitespace)
add summary (major release only)
add issue links with md-issue-refs
bump changelog links at the top
commit - github binary install docs (doc/ghrelnotes.md) up to date and pre-tested
- Install page (site/src/install.md) up to date and pre-tested
- draft announcement for chat, mail list, mastodon etc (doc/ANNOUNCE)
- release tags
just reltags - release binaries built from tag
just ghbin, wait for all to succeed - Install page (site/src/install.md) –version examples match release binaries
- draft binaries building started
-
4. published
- relevant release branch work cherry-picked to master branch
changelogs, relnotes, announcements - all packages uploaded correctly to hackage
just hackageupload - master branch pushed to github
- new manuals published and rendering/redirecting correctly
- site repo pushed to github
- main and site repos auto-pulled to hledger.org, site rebuilt
hledgerorgsh grep release.= /opt/hledger/site/out/js/site.js - https://www.hledger.org/js/site.js showing latest version
curl -s https://hledger.org/js/site.js | grep release.=
purge cache at https://dash.cloudflare.com/f629035917dd3b99b1e37ae20c15ff09/hledger.org/caching/configuration (major release only) - default manual urls redirecting to latest version (major release only)
hledgerorgsh sh -c 'systemctl stop caddy; systemctl start caddy'curl -sI https://hledger.org/hledger.html | grep location
- release branch pushed to github
- release tags pushed to github
just reltags-push VER - github draft release with release binaries attached
https://github.com/simonmichael/hledger/releases/new (XXX safari may not show new tag, may need brave)just ghrel-notes(in release branch)just ghbin-downloadjust ghrel-bin-upload VER - github release published
decide if release should be immutable (artifacts attached, all correct ?) then adjust repo settings
review, publish - github nightly release updated (XXX nightly release deleted, needs reviving)
in master, update changes link in doc/ghnightlynotes.mdjust nightlyrel-notes - install instructions tested and working
- stack
- cabal
- source checkout
- github release > How to install, each platform
- announced
- mail list(s) [email protected] for major (+ [email protected] for supermajor)
- matrix
- irc
- mastodon
- pta forum
- relevant release branch work cherry-picked to master branch
-
5. cleanup and support
- review/polish/sync changelogs & relnotes
- new version, man dates, dev tag in master (major version only)
j devtag-push - RELEASING.md checklist/notes updated
- monitor/support/handle issues: issue tracker, matrix, irc, mail list, forum, reddit
Some more good things to do after a release:
- hledger.org site/doc updates
- hledger_finance repo updates
- plaintextaccounting site updates (eg project stats)
How to..
More procedure notes.
Check release readiness
- Any blocking open issues ? https://bugs.hledger.org
- Any blocking open PRs ? https://prs.hledger.org
- Any blocking items on https://hledger.org/ROADMAP.html ?
- Any blocking items in personal notes & backlogs ?
Check tools are up to date
- Check for consistent stackage snapshot(s) and extra deps used in stack.yaml, Shake.hs, hledger-install.sh, bin scripts, tools scripts
- Shake binary is up to date
./Shake.hs hpack --versionmatches the one instack --version
Run local tests
just testjust doctestjust haddocktest
Run CI tests
- push to a PR, wait for green
- or push to
cibranch, wait for green at https://ci.hledger.org - or
just push(pushes toci, then tomaster)
Run release branch tests
just teststack exec -- hledger --version, check version, hash, release date, no ‘+’stack exec -- hledger help | tail, check version, month matches release
Update the Install page
In site repo:
- update
install.md- query-replace OLD -> NEW in
- “current hledger release”
- CI binaries badges/links, including linux-static-arm32v7 if built
- “building from source”
- stack install command
- cabal install command
- query-replace OLD-brightgreen -> OLD-red
- only after release binaries are built (preferably after release is published): update –version outputs (version, hash, date, but not platform)
- final output line from
hledger test(run local build and in terminal for normal speed) - Total count from
make functest - preview
- commit:
install: NEW
- query-replace OLD -> NEW in
Update hledger in stackage
- monitor packaging status in lts and nightly: https://www.stackage.org/package/hledger
- update https://github.com/fpco/stackage/blob/master/build-constraints.yaml as needed
Update RELEASING.png
- edit RELEASING.canvas in obsidian
- CMD-p > Export as image, don’t show logo
- commit
Tips
-
Release, or practice releasing, often to improve the process.
-
Use and continually update RELEASING.md. Document procedures and gotchas to save time and enable automation in future.
-
Also the diagram (RELEASING.canvas, made with Obsidian).
-
But don’t document prematurely or in too much detail.
-
Make things a little better each time through: simpler, more reliable, better documented, more automated, easier, faster, cheaper, higher quality.
-
Optionally save this file as RELEASING2.md and update notes there until after release, if it’s interfering with git branch switching.
-
Use and update scripts, in
Justfile,Shake.hs,tools/etc. -
Do all releases from a release branch.
-
Update dev changelogs frequently in master. Finalise changelogs in the release branch. Merge back to master after release. (Related older doc: CHANGELOGS)
-
All release binaries should be built from the release-tagged commit. The binaries’ –version output should match the release tag and release date.
-
Try to do only full releases including all four main hledger packages; partial releases add complexity.
-
Try to avoid pre-announcing a hard release date. It will always take more time than you think, if you go late you might miss your intended date in many timezones, and there’s no point adding unnecessary pressure.
-
The biggest potential time sinks are:
- reviewing/relearning the process/docs/infrastructure
- updating/improving the process/docs/infrastructure
- preparing changelogs
- building binaries for all platforms
- troubleshooting github workflow issues
- followup work due to release mistakes, bugs in new features, or regressions
-
Hard/risky/intensive tasks should happen without time pressure; during the final countdown, things should be easy.
Release manager activities
These have complex interdependencies and sequencing constraints. Chunk, separate, routinise, document and automate them as far as possible.
| Software | selecting changes, packages, release dates; coordinating contributions; ensuring release readiness |
| Branch Management | coordinating main and release branch, local and remote repos, CI branches |
| Version Bumping | choosing and applying new version numbers and related things like tags, github releases, urls, ghc and dep versions, stackage resolvers, everywhere needed |
| Docs | command help, manuals, changelogs, release notes, github release notes, install page, install scripts, announcements, process docs |
| Testing | local testing, CI testing, extra release-specific testing |
| Artifacts | generating binaries, zip files, github releases etc. |
| Publishing | uploading, pushing, making visible, finalising |
| Announcing | various announcement stages and channels |
Glossary
Some standard terminology, useful when precision is needed, eg in release scripts.
General
release
A snapshot of the software and related artifacts like executable binaries, which is named, tagged, documented, announced, and usually picked up by packaging systems on various platforms.
version control system, VCS
A tool used for storing and sharing and viewing the history and different lines of development of a software project, or other set of files. hledger uses Git.
repository, repo
A set of files being stored and managed by a VCS. Often published on a repository hosting service, such as Github.
working copy, clone
A local copy of a repository’s files. Typically each developer has one or more of these, and can share changes easily with the official public repository.
branch
Some VCS’s, including Git, can store multiple branching lines of development within one repository. A working copy can be quickly switched to a different branch to show its content.
master, main
The main branch in a repo, usually named master or main. Pull requests are usually relative to this.
pull request, PR
A request to merge a development branch with master, and any related discussion. On Github, these are kept alongside issues in the issue tracker.
continuous integration, CI
Automated actions that run when new code is pushed to a shared repo, such as running tests or producing binaries. On Github this is called Github Actions and action scripts are called workflows.
release engineering
https://en.wikipedia.org/wiki/Release_engineering
hledger-specific
package
A releasable unit of Haskell software. hledger has several core packages usually released together: hledger-lib, hledger, hledger-ui, hledger-web.
hledger version number
A 2-4 part dotted number naming a hledger release or hledger package version: MA.JOR[.MINOR[.FIXUP]] or MA.JOR.99[.PREVIEW] where 99 means “unreleased (MAJOR+1)”. See examples below.
hledger version string
A line of text describing a hledger binary, shown by --version. It contains program name, version number, commit hash and date, machine architecture etc. Eg: hledger 1.24.1-g7799d526b-20211210, mac-x86_64
Full release
A release of all four core hledger packages (hledger-lib, hledger, hledger-ui, hledger-web). Major and preview releases are always full releases.
Partial release
A release of just some of the hledger packages. Bugfix and fixup releases are sometimes partial.
Single-version release
A release where all packages have the same version. Major and preview releases are always single-version.
Mixed-version release
A release where the packages have different versions, because of a previous partial release. Bugfix and fixup releases are sometimes mixed-version.
changelog
A CHANGES.md file listing the release history and the changes in each release. There is one for each hledger package and one for the hledger project as a whole.
release notes
The Release Notes page on the hledger website: the combined release history of the core hledger packages, showing user visible changes only.
Releases and builds
Major release
Major releases include new features and incompatible API changes, and normally happen at the start of each quarter’s third month (3/1, 6/1, 9/1, 12/1). Example version number: 1.25
Bugfix release
Bugfix releases include only bug fixes, without API changes. These happen when needed, to fix significant bugs in the previous major release. Example version number: 1.25.2 (“second bugfix release for 1.25”)
Fixup release
Fixup releases fix packaging errors, with no changes to the hledger software. Example version number: 1.25.0.1 or 1.25.2.1 (“first fixup release for 1.25 / 1.25.2”).
These should rare; we basically never do these.
Preview release
A preview of the upcoming major release for testers/early adopters, and a test of the release process, published on Github. Not a formal hledger release, eg not published on Hackage, usually not packaged, no bugfix releases, no regression bounties, not shown in release notes. These typically appear in the quarter’s first and second month if needed. Example version number: 1.25.99.1 (“preview 1 of 1.26”)
CI binaries
Temporary downloadable binaries produced by a run of the linux/mac/windows workflows in the hledger repo. This may happen periodically, eg weekly. Downloading requires a Github login.
Dev build
A local developer build of unreleased code. This is typically in master or a development/PR branch. Example version number: 1.25.99 (“unreleased 1.26-dev”)
Repos and branches
hledger repo
The hledger git repository, containing the hledger software, reference manuals, and developer docs. https://github.com/simonmichael/hledger
site repo
The hledger_website git repository, containing most of the hledger website which appears at https://hledger.org. Usually checked out under the hledger repo as site/. https://github.com/simonmichael/hledger_website
finance repo
The hledger_finance git repository, containing the hledger project’s financial ledger. Usually checked out under the hledger repo as finance/. https://github.com/simonmichael/hledger_finance
master
The branch named master in the hledger repo; the main line of hledger development. Pull requests are usually relative to this.
release branch
Branches named MA.JOR-branch in the hledger repo, eg 1.25-branch. Releases and release previews are always made from a release branch.
Repositories
Source code and content related to hledger are kept in revision control repositories, mostly using Git.
The official repos:
-
The main repo: hledger, hledger-ui & hledger-web code, user manuals, developer docs.
https://github.com/simonmichael/hledger (shortcut url: http://code.hledger.org) -
The site repo: hledger.org website, additional hledger docs.
https://github.com/simonmichael/hledger_site (http://site.hledger.org) -
The finance repo: project financial journals and reports.
https://github.com/simonmichael/hledger_finance (http://finance.hledger.org)
Other repos exist for third-party hledger tools (hledger-iadd, hledger-interest, etc.) and packages.
ROADMAP
Ideas of where the hledger project should be going next. Being listed here suggests a bit of commitment, perhaps even a schedule.
See also: ACHIEVEMENTS
2025
- progress on automated lot tracking
- fix hledger-ui –watch memory leak
- up-to-date, effective project finance reports
- easier file/script management when importing
- easier setup & get started process
2024
Targets:
- hledger 1.41, december
- hledger 1.40, september
- hledger 1.34, june
- hledger 1.33, april
2023
Targets:
- hledger 1.32, december
- hledger 1.31, september
- hledger 1.30, june
- demos: built in asciinema demos and maintenance process done, 4 simple demos
- ghc 9.6 support partly done, hledger-web is blocked on deps
- process/tools improvement done
- docs improvement done: 115 doc commits + 112 site commits, manual cleanups & rewrites
- hledger 1.29, march
Goals:
- CSV extensibility: workflows to obtain, use, develop, share, contribute ready-to-use CSV rules
- Scripts extensibility: workflows to obtain, use, develop, share, contribute ready-to-use scripts
- Interop: clear ledger & beancount import/export how-tos documenting issues & workarounds
- Better installer: more robust, binary-installing
- Bar charts: simple built in bar charts
- Investment: clear updated how-to documenting available tools & best practices for common needs (price fetching, lot reducing, lot reporting, cost reporting, gains reporting)
Priorities:
- newcomer/learner experience: docs, installers, demos
- customiser/contributor experience: easy csv rules install/contrib, scripts install/contrib
- maintainer experience: reduce tech/doc/process/issue debt, increase velocity
- marketing/community: news updates, mastodon presence
- interop: solve Ledger/Beancount reading/writing/conversion
- features: charts, investment
Mission:
- Make plain text accounting more usable and useful for all
- Bring relief to people experiencing financial and financial technology stress
- Help people and communities in all countries increase their financial mastery and freedom
- Help grow a shared global culture of accountability and sustainability
- Starting with this project and ourselves.
2020
Targets:
- hledger 1.19, september
- account transactions register, stricter/more correct handling of unbalanced multicommodity transactions (#1177), Track & show deposited lots (#1022), Report unrealized capital gains/losses (#1029)
- hledger 1.18, june
- more effective CI setup, updated home page, quickstart, tutorials etc., negative matching in CSV rules,
- hledger 1.17, march
- field matching in CSV rules, reduce install hassles with terminfo C lib (?), more import/export options, simple console charts, refreshed home page, faq, tutorials, manuals,
Priorities:
- Documentation: Improve the docs.
- Effectiveness: Improve getting-started experience, just-works quality, practicality, real-world usefulness.
- Investment: Improve suitability for investment tracking (#1015)
- Charts: Add charts and more visual appeal.
- Correctness: More support for enforcing correctness & accounting rules.
2019
Targets:
- hledger 1.16, december
- ghc 8.8 support, more powerful CSV conversion, updated home page, faq, manuals, reduce install hassles with terminfo C lib
TESTS
About testing in the hledger project, as of 201809.
Kinds of tests
- Isolated — tests should return the same results regardless of the order in which they are run.
- Composable — if tests are isolated, then I can run 1 or 10 or 100 or 1,000,000 and get the same results.
- Fast — tests should run quickly.
- Inspiring — passing the tests should inspire confidence
- Writable — tests should be cheap to write relative to the cost of the code being tested.
- Readable — tests should be comprehensible for reader, invoking the motivation for writing this particular test.
- Behavioral — tests should be sensitive to changes in the behavior of the code under test. If the behavior changes, the test result should change.
- Structure-insensitive — tests should not change their result if the structure of the code changes.
- Automated — tests should run without human intervention.
- Specific — if a test fails, the cause of the failure should be obvious.
- Deterministic — if nothing changes, the test result shouldn’t change.
- Predictive — if the tests all pass, then the code under test should be suitable for production.“ –Kent Beck
-
Unit tests
Unit tests exercise small chunks of functionality. In hledger, that means a function. So, many of our functions have one or more unit tests. These are mostly in hledger-lib, with a few in hledger.
Our unit tests use the tasty test runner, tasty-hunit HUnit-style tests, and some helpers from Hledger.Utils.Test, such as:
testsandtestaliases fortestGroupandtestCaseassert*helpers for constructing various kinds of assertions
We would like our unit tests to be:
- easy to read (clear, concise)
- easy to write (low boilerplate, low cognitive load)
- easy to maintain (easy to edit, easy to refactor, robust)
- easy to associate with the code under test (easy to view/jump between code & test, easy to estimate coverage)
- and scalable (usable for all devs, easy to run and select, suitable for small/large modules/packages).
Here's the current pattern (let us know if you see a better way):
module Foo ( ... tests_Foo -- export this module's and submodules' tests ) where import Hledger -- provides Hledger.Utils.Test helpers import Bar -- submodules, providing tests_Bar etc. import Baz functionA = ... functionB = ... functionC = ... functionD = ... tests_Foo = tests "Foo" [ -- define tests at the end of each module -- a group of several named tests for functionA tests "functionA" [ test "a basic test" $ assertBool "" SOMEBOOL ,test "a pretty equality test" $ SOMEEXPR @?= EXPECTEDVALUE ,test "a pretty parsing test" $ assertParseEq PARSER INPUT EXPECTEDRESULT ,test "a multiple assertions test" $ do A @?= B doSomeIO C @?= D ] -- a single test containing multiple unnamed assertions for functionB ,test "functionB" $ do assertBool "" BOOL EXPR @?= VALUE ,tests_Foo -- aggregate submodule tests ,tests_Bar ]Here are some real-world examples.
The unit tests are shipped as part of the hledger executable, and can always be run via the test command (
hledger test). -
Doc tests
Like unit tests, but defined inside functions' haddock documentation, in the style of a GHCI transcript. These test functionality, provide usage examples in the API docs, and test those examples, all at once. They are a bit more finicky and slower than unit tests. See doctest for more.
doctests do not work on Mac with GHC 8.4+, out of the box. See ghc#15105 for current status and a workaround.
-
Functional tests
Functional tests test the overall functioning of the program. For hledger, that means running
hledgerwith various inputs and options and checking for the expected output. This exercises functionality in the hledger and hledger-lib packages. We do this with shelltestrunner. Tests are defined in files named*.testunder hledger/test/, grouped by component (command or topic name). For more about these, see the README there. -
Code tests
We have some tests aimed at testing eg code quality, generally runnable via just. Eg
just haddocktest,just hlinttest. -
Package test suites
Haskell tools like stack and cabal recognise test suites defined in a package's cabal file (or package.yaml file). These can be run via
stack test,cabal testetc., and they are required to build and pass by services like Stackage. Here are the currently hledger package test suites:
package test suite what it runs hledger-lib doctests doctests hledger-lib easytests unit tests hledger test builtin test command (hledger's + hledger-lib's unit tests) hledger-ui
hledger-web
Coverage
This means how thoroughly the code is tested - both in breadth (are all parts of the code tested at least a little ?) and in depth (are all possible code paths, states, situations tested ?).
Our current test coverage can be summarised like so:
package unit doc functional
hledger-lib X X X
hledger X X
hledger-ui
hledger-web
There are ways to generate detailed coverage reports for haskell unit tests, at least. It would be useful to set this up for hledger.
How to run tests
Run unit tests:
$ just unittest
Run doctests:
$ just doctest
Run functional tests (and unit tests, now):
$ stack install shelltestrunner
$ just functest
Run the package tests (unit tests, maybe doctests, but not functional tests) of all or selected packages.
$ stack test [PKG]
Run "default tests: package plus functional tests":
$ just test
Test generation of haddock docs:
$ just haddocktest
Run built-in hledger/hledger-lib unit tests via hledger command:
$ hledger test # test installed hledger
$ stack build hledger && stack exec -- hledger test # test just-built hledger
$ hledger test --help
test [TESTPATTERN] [SEED]
Run the unit tests built in to hledger-lib and hledger,
printing results on stdout and exiting with success or failure.
Tests are run in two batches: easytest-based and hunit-based tests.
If any test fails or gives an error, the exit code will be non-zero.
If a pattern argument (case sensitive) is provided, only easytests
in that scope and only hunit tests whose name contains it are run.
If a numeric second argument is provided, it will set the randomness
seed for easytests.
Rebuild and rerun hledger/hledger-lib doc tests via ghcid:
$ just ghcid-doctest
See all test-related just rules:
$ just h test
Version numbers
Some places version numbers appear:
- –version (and sometimes –help) output of all hledger* executables
- web manuals on hledger.org
- download page
- changelogs
- release notes
- release announcements
- hackage/stackage uris
- cabal tarball filenames
- platform-specific packages
Some old version numbering goals:
- automation, robustness, simplicity, platform independence
- cabal versions must be all-numeric
- release versions can be concise (without extra .0’s)
- releases should have a corresponding VCS tag
- development builds should have a precise version appearing in –version
- development builds should generate cabal packages with non-confusing versions
- there should be a way to mark builds/releases as alpha or beta
- avoid unnecessary compiling and linking
- minimise VCS noise and syncing issues (commits, unrecorded changes)
Current version numbering policy:
-
We (should) follow https://haskell.org/haskellwiki/Package_versioning_policy
-
The “full release version” is ma.jor.minor, where minor is 0 for a normal release or 1..n for bugfix releases. Each component is a natural number (can be >= 10). Eg: 1.13 major release, 1.13.1 bugfix release.
-
The “release version”, which we prefer to use when possible, is just ma.jor when minor is 0. Ie elide the dot zero.
-
The build version is ma.jor.minor+patches, where patches is the number of patches applied in the current repo since the last release tag.
-
hledger --versionshows the release version or build version as appropriate. -
Release tags in the VCS are like PKG-VERSION. Eg hledger-1.13,
-
hledger-ui-1.13.1.
Current process:
-
In each hledger package directory there’s a
.versionfile containing its desired version number. -
After changing a
.versionfile: run./Shake setversionto propagate the versions to all other places in the packages where they should appear. This is not perfect (see Shake.hs) so review and manually adjust the proposed changes before committing. Those places include (you can also run these rules individually):-
PKG/package.yamlcontains the cabal package version declaration, bounds on other hledger packages, and a CPP VERSION macro used inhledger/Hledger/Cli/Version.hs. Changes in package.yaml will be propagated toPKG/PKG.cabalon the next stack build or –dry-run build or withjust cabalfiles. -
PKG/.version.m4contains the version macro used in documentation source files (*.m4.md). It is updated by./Shake setversion. -
PKG/.date.m4contains the monthyear macro used in man pages. It is updated by./Shake manuals.
-
-
At release time:
-
./Shake PKG/CHANGES.md-finaliseconverts the topmost heading, if it is an interim heading (just a commit hash), to a permanent heading containing the version and today’s date. -
for each package being released, a PKG-VERSION git tag is created.
-
-
At major release time:
- A new snapshot of the reference docs is added to the website, by
./Shake site/doc/VERSION/.snapshot, and added to the links insite/js/site.js.
- A new snapshot of the reference docs is added to the website, by
























