Fast, friendly, robust
plain text accounting software

hledger is...

Here's more about the Features. Don't hesitate to join the chat and ask questions.

Github repo GitHub downloads GitHub downloads, latest Hackage Stackage
CI binaries-linux-x64-static CI binaries-mac-x64 CI binaries-windows-x64

Quick start

Welcome! This plain text accounting stuff is useful and more fun than it sounds - care to give it a try ?

Install, then see Get Started, or the Examples below, or run hledger to see help and demos. Full documentation is ready when you need it, in the sidebar to the left. (If not visible, click/tap the horizontal-lines icon at top left.)


Here are three transactions in journal format, recorded in the journal file ($LEDGER_FILE or ~/.hledger.journal) by hledger add or other method. The account names and amounts are separated by at least two spaces; a positive amount means "added to this account", negative means "removed from this account". hledger will check that each transaction's amounts sum to zero; one of them may be omitted for convenience.

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.

You can run reports like so:

$ 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 
$ 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 
$ 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


If you use other account names, it's useful to declare their account types:

account actifs                          ; type:Asset, 2+ spaces required before the ;
account actifs:banque:compte courant    ; type:Cash
account actifs:banque:compte d'épargne  ; type:Cash
account actifs:espèces                  ; type:Cash
account passifs                         ; type:Liability
account capitaux propres                ; type:Equity
account revenus                         ; type:Revenue
account dépenses                        ; type:Expense

Or declare all accounts, currencies and tags, if you want strict error checking:

account assets                   ; type:A, 2+ spaces required before the ;
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: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

commodity $1000.00

tag type
$ hledger check --strict

Declaring accounts also helps set their preferred display order:

$ hledger accounts -t
  credit card

You can declare account aliases to save typing:

alias chk  = assets:bank:checking
alias cash = assets:cash
alias card = liabilities:creditcard
alias food = expenses:food


2023-02-15 market
    food          $50

Other UIs

Instead of using the command line, you could run hledger-ui or hledger-web. Here are the command line, terminal, and web interfaces, with more complex data:

Time tracking

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

Or in timedot format:

biz:research  .... ..
fos:hledger   .... .... ....

fos:ledger    0.25
fos:haskell   .5
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 

CSV import

hledger can read CSV (or SSV, TSV, or other character-separated) files representing transactions:

"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

 account2 expenses:food

 account2 assets:bank:savings
$ hledger -f bank.csv print
2023-02-22 DEPOSIT
    assets:bank:checking          $50.00
    income:unknown               $-50.00

    assets:bank:checking         $-10.00
    assets:bank:savings           $10.00

The import command detects and adds just new transactions to the journal (works with most CSVs):

$ hledger import bank.csv
imported 2 new transactions from bank.csv
$ 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

More examples...

See also

Site tips

  • Use the horizontal lines icon at top left to toggle the sidebar.
  • Use the paintbrush icon to change theme.
  • Use the magnifying-glass icon to search.
  • Access keys are also available:
    s toggle sidebar, t theme, / search,
    1 home page, 2 recent changes, < previous page, > next page.


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

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.



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


  • imp: -I can now be overridden by -s or check
  • imp: commands list updates
  • doc updates: commands list, report start & end date, smart dates, balance command
  • site updates: install, FAQ reorg



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


  • fix: show trailing decimal mark on cost amounts too, when needed
  • imp: Revert problematic process- 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


You can see recent discussions via the links at https://hledger.org/support.html.


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


  • 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.


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.


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


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 >= for HSEC-2024-0003. Updated docs: balance assertions, balancesheetequity.

plaintextaccounting.org updates: FAQ, Borrowing and lending, Investing and trading, Shared expenses, project ledger.


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.


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.


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.


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.


  • 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.


  • 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



I tried getting hledger-sankey (generates Sankey diagrams of cash flows) working on my real world ledger, and contributed a patch.


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.


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.


  • 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..

Mail list

  • 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


  • 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


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..

Mail list

  • 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..

Hacker News


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.

Mail list

  • 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..


  • 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


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..

Mail list

  • 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.


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..


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


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


  • 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 close design/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.


  • 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.


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


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

Mail list:

  • 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.


  • 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 add does not add a default commodity symbol #815
  • auto postings: clarify
  • examples, examples/invoicing: expand READMEs, clarify status

Forgot to mention last week:

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/



  • 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:

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.


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").



Added easy tagged time logging to timedot format, using letters instead of dots.


Update the stars.hledger.org redirect (we have reached the top 30 Haskell projects by github stars 🌟 🎉).


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.


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").




  • 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/--outputfile with a .json or .sql extension now properly selects those formats.


  • The print command has a new beancount output 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.


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.


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.



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.


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.


Spent some time drafting alternate hledger manual structures and thinking about adding a user guide.


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.



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.


My time log for the week shows 69% enhancement, 27% cleanup, 5% support.


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.


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



  • 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.


  • 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



  • 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


  • 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.


  • Contemplating: merging cookbook and examples/*; something more custom to replace mdbook


  • hledger-report1: a custom compound report script, haskell and bash versions


  • exploring latest Paisa, and how to use it with existing setups


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:


  • 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!)


  • 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.


  • 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


  • 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).




  • 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



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

What I need to do my homework on

  • The limitations of balance assertions in HLedger
  • study haskell

What makes me cry about HLedger

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.


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 hledger's Accounting concepts page and Accounting links.

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.


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 more fun for myself, Ledger was under-maintained and using and supporting it was too frustrating, and I wanted to work with Haskell not C++.

Slightly longer:

  • To provide a more usable, robust, documented, cross-platform-installable version of Ledger .
  • To provide a more maintainable and hackable version of Ledger for developers.
  • To provide a useful library and toolbox for finance-minded Haskell programmers.
  • To explore the suitability of Haskell for such applications.
  • To attempt to build a successful, solvent, self-supporting project in a thriving ecosystem of financial software projects.

See also: hledger and Ledger: History.

What is the hledger project's current mission and plans ?

  1. Help make plain text accounting more usable and useful for all.
  2. Bring relief to people experiencing financial and financial technology stress, by providing dependable, empowering accounting tools, learning materials, and community.
  3. Help people and communities in all countries increase their financial mastery and freedom.
  4. Help grow a shared global culture of accountability and sustainability.
  5. Starting with this project and ourselves.

Here is the ROADMAP.


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 ?

hledger can provide clarity and insight into your personal or business finances, time logs, or other dated quantitative data, with relatively little effort on your part. You need only provide a list of transactions, as a plain text file in a simple human-readable format. (Or a CSV file plus some conversion rules.) From this hledger can generate a variety of useful reports and interactive views. See Features.

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:

  1. Manually download recent CSVs from your bank's website
  2. hledger import ACCT1.csv ACCT2.csv ...
  3. review/clean up the new entries in your journal.

Isn't plain text ugly and hard to use ?

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.

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 > Debits and credits.


How does hledger relate to Ledger ?

hledger (begun 2006) is inspired by, and a friendly coopetitor of, John Wiegley's Ledger (begun 2003). It is an attempt to rewrite Ledger in a more expressive programming language and take it to the next level in usability and practicality. See hledger and Ledger.

What is/was ledger4 ?

hledger has its own parser for a file format close to Ledger's. In 2012 John Wiegley prototyped a more exact conversion of Ledger 3's parser to Haskell, calling it ledger4. For a while I integrated this as an alternate file format within hledger, hoping to improve our ability to read original Ledger files, but the parser needed lots more work to become useful, so later I removed it again.

How is hledger different from / interoperable with... ?

See Cookbook > Other software for notes on Ledger, Beancount, GnuCash, Quickbooks, etc. Also:

How could I import/migrate from...

How could I export/migrate to...


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 Get Started 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 hledger: Videos.

How do I set the default file to something other than ~/.hledger.journal ?

See hledger manual: Setting LEDGER_FILE.

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 does this entry give a "no amount" error even though I wrote an amount ?

  a $1

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

    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.


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

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).

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.


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 report inflows and outflows separately ?

Use two register reports with an amt: query. Eg:

hledger register 'amt:<0'
hledger register 'amt:>0'

How do I show transactions where money left an account ?

You can use register with an account and amount query:

hledger register cash 'amt:<0'

If you prefer aregister, write it this way (because of aregister's special first argument):

hledger aregister cash cash 'amt:<0'

Printing the full transactions with print is difficult, since print is a transaction-based report that matches transactions with any matched postings.

You can do it in Emacs ledger-mode with C-c C-f (or M-x ledger-occur) and a regular expression. Eg, to show just the transactions where cash was decreased:

C-c C-f :cash.*- *[1-9]

For now, this seems to be the best approach at the command line too: filter the output of hledger print ACCT, keeping only transactions where ACCT and a negative (or positive) amount appear on the same line. This means using awk or some other unix tool that can treat transactions as multi-line records. (Example welcome)

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 process individual single-account postings, not the multi-account transactions. Instead, use two commands, eg:

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 it discards directives. The output will normally be a valid journal, but it can have a different meaning or even be unparseable due to:

  1. Loss of decimal-mark directives, which could disrupt number parsing.
  2. Loss of commodity directives declaring display precisions, which could disrupt transaction balancing.
  3. Loss of account directives declaring accounts' types, which could alter reports.
  4. Balance assertions which break because you have excluded transactions they depend on.


  • Whenever you use -f - to read hledger print output, also add -I to 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 another input:

      $ hledger print ... | hledger -f- -I -f2023accounts.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 ...


Is there a config file ?

So far, no, we have resisted this. In short, because config files add complexity and variance; it's better to just work the best way in the first place. There are ways power users can emulate them, and I'm of the opinion config files would be a net loss for productivity, overall.

Despite this, it would probably be a very popular feature. If you want it to happen, feel free to make a case for it and propose a specification, in chat or #1013.

Below are some ways to emulate a config file.

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.

How can I always run hledger with certain general options ?

You can run hledger via a small script. Since you will likely have other scripts or applications that run "hledger", it's most robust if the script is also called hledger. Here's how to do it on unix systems: somewhere in your PATH before the actual hledger executable, eg in ~/bin, create a hledger script that runs the actual hledger, with your preferred options/arguments (plus any others provided). Something like this:

/usr/local/bin/hledger -I --infer-equity --infer-costs --infer-market-prices "$@"

and make it executable:

$ chmod +x ~/bin/hledger

Unfortunately... this fails with commands that don't support the usual general options, like hledger add, and many add-ons. Back to the drawing board.

How can I always run hledger commands with certain command options ?

Make a shell alias or script for that command. Eg in your shell config:

alias print=`hledger print -x`

or in ~/bin/print:

hledger print -x "$@"

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


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.


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").

Videos, Talks

hledger-related videos:

Audio only:

See also:


The current hledger release is 1.33.1. Here are the release notes.

There are three main ways to install hledger on your computer:

After installing, here are some final things to check. Your feedback helps make this process smoother.

Release binaries

Official release binaries for Linux, Mac, Windows.

hledger release binaries release binaries (RSS RSS)

Packaged binaries

Homebrew (Linux, Mac, WSL)

Homebrew brew install hledger

Docker (Linux, Mac, Windows)

Docker docker pull dastapov/hledger


Scoop scoop install hledger
Chocolatey choco install hledger -y
Winget winget install simonmichael.hledger


Alpine edge doas apk add hledger hledger-ui hledger-web
Alpine 3.19
Alpine 3.18
Alpine 3.17
enabling the community repository

Arch pacman -Sy hledger hledger-ui hledger-web

Debian unstable sudo apt install hledger hledger-ui hledger-web
Debian stable
Debian oldstable
Debian oldoldstable

Fedora_41 sudo dnf install hledger
Fedora 37

Gentoo sudo layman -a haskell && sudo emerge hledger hledger-ui hledger-web

Raspberry Pi release binaries hledger-linux-arm32v7.zip
Raspberry Pi contributed binaries hledger-aarch64-manjaro.gz hledger-armhf32-debian.gz (unaudited)

ubuntu_24_04 sudo apt install hledger hledger-ui hledger-web

Void Linux x86_64 xbps-install -S hledger hledger-ui hledger-web


freebsd ports pkg install hs-hledger hs-hledger-ui hs-hledger-web
netbsd package pkg_add hledger
openbsd ports pkg_add hledger

Nix (Linux, Mac)

Nix nix-shell -p hledger hledger-ui hledger-web
Issues: #1030, #1033, #2089, Troubleshooting

Sandstorm (Web)

Sandstorm HLedger Web sandstorm app

Build from source

Build requirements

Building hledger requires the GHC compiler and either the stack or cabal build tool. These are usually easy to install (see below). But they will require quite a bit of memory and disk space - up to 4G of RAM and 2G of your hard drive to build the hledger tools. See also the build tips below.

On unix systems, you will need a UTF-8-aware locale configured. Also you may need to install additional C libraries to avoid errors like "cannot find -ltinfo" when building hledger. Eg,

  • on Debian or Ubuntu: sudo apt install libgmp-dev libtinfo-dev zlib1g-dev
  • on Fedora or RHEL: sudo dnf install gmp-devel ncurses-devel zlib-devel

Here are some ways to build hledger:

Build with hledger-install

hledger-install.sh is an automated install script that requires only bash. This is a good choice if you are not used to building Haskell software. It installs build tools if needed, then builds the current release of the hledger tools and some add-on tools, installing them in ~/.local/bin (or ~/.cabal/bin if you had cabal and not stack installed).

curl -O https://raw.githubusercontent.com/simonmichael/hledger/master/hledger-install/hledger-install.sh
less hledger-install.sh   # <- optional, good practice: inspect downloads before running
bash hledger-install.sh

Build with stack

stack is a reliable Haskell build tool. You can install it

  • with your system package manager
  • or from its website
  • or with ghcup (good if you plan to do a lot of Haskelling).

Once stack is installed, run these commands:

stack update
stack install hledger-lib-1.33.1 hledger-1.33.1 hledger-ui-1.33.1 hledger-web-1.33.1 \
  --resolver=nightly-2024-05-16 --install-ghc --verbosity=error

stack will install a compatible version of the GHC compiler if needed, using 1-2G of disk space under ~/.stack, or if stack is configured for ghcup, under ~/.ghcup. Then it will build the hledger tools and install them in ~/.local/bin.

Build with cabal

cabal is another popular Haskell build tool. You can install it with your system package manager or with ghcup. You will also need to install a supported version of GHC: any version from 8.10.7 to 9.8, with newer versions preferred. Then:

cabal update
cabal install alex happy
cabal install hledger-1.33.1 hledger-ui-1.33.1 hledger-web-1.33.1

This will build the hledger tools and install them in ~/.cabal/bin.

Build in a source checkout

If you'd like to customise or review the hledger source code, it's better to get it yourself with git. Eg the latest development version:

git clone https://github.com/simonmichael/hledger

or the latest release:

git clone https://github.com/simonmichael/hledger -b 1.33.1


cd hledger

and you can build and install with:

stack update
stack install


cabal update
cabal install alex happy
cabal install all:exes

Build in a Docker container

This will use a docker image with the necessary build tools pre-installed:

cd hledger/docker
./build.sh   # or build-dev.sh to keep build artifacts

Build 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 -j1 to 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, by omitting hledger-ui and hledger-web.
  • It's ok to kill a build and rerun the command later; you won't lose progress.
  • You can add --dry-run to stack/cabal/nix install commands to see how much building remains.
  • If you have previously installed the hledger tools, they will usually be overwritten by the new version. If you have them installed in multiple places in your PATH, you may see a warning, reminding you to remove or rename the old executables.

Here are some known build issues and workarounds on various platforms (possibly obsolete):

Check your installation

After installing, run the hledger tools and verify that their versions are what you just installed (and not older versions from a previous install). Eg:

$ hledger --version
hledger 1.33.1-...
$ hledger-ui --version
hledger-ui 1.33.1-...
$ hledger web --version
hledger-web 1.33.1-...

You can run the built-in unit tests if you'd like:

$ hledger test
All 227 tests passed (0.04s)

or the more thorough functional tests, if you are set up for working with the hledger source code:

$ just functest
 Total   1047 ...
functest PASSED


If the hledger tools won't run at the command line or you don't see the expected --version output, check that the newly installed executables are first in your shell's PATH. After building/installing, you may have seen a message about where the executables were installed. Eg:

  • If you installed with stack, it's $HOME/.local/bin (on Windows, %APPDATA%\local\bin)
  • If you installed with cabal, it's $HOME/.cabal/bin (on Windows, %APPDATA%\cabal\bin)
  • If you installed with nix, it could be $HOME/.nix-profile/bin

Make sure that this bin directory is included in your shell's PATH setting, and preferably near the start of the list, to preempt any old hledger binaries you might have lying around. How to configure this depends on your platform and shell. Eg if you are using bash, this will show $PATH:

echo $PATH

and this will add the stack and cabal install dirs to it permanently:

echo "export PATH=~/.local/bin:~/.cabal/bin:$PATH" >> ~/.profile 
source ~/.profile

Here's how to set environment variables on Windows.


On most unix systems, when you are processing non-ASCII text with hledger, the LANG environment variable must be set to a UTF-8-aware locale, to avoid errors like "invalid byte sequence" or "mkTextEncoding: invalid argument". (This applies to the Haskell build tools like GHC, cabal and stack, as well.) Check that your LANG setting mentions UTF-8, and if not, change it. Eg:

$ echo $LANG
$ export LANG=C.UTF-8    # or en_US.UTF-8, fr_FR.utf8, etc.
$ echo $LANG

In some cases the locale may need to be installed with your system package manager first. See hledger: Troubleshooting for more help.

On Microsoft Windows, if you see such error messages, perhaps this doc can help.

On Nix or GUIX, the procedures are different.

Next steps

Nicely done! Now see Get started, or come to the #hledger chat where we'll gladly share tips or receive your feedback.

Release notes

Major releases and user-visible changes, collected from the changelogs ( hledger, hledger-ui, hledger-web ). Changes in hledger-install.sh are shown here.

2024-05-02 hledger-1.33.1

hledger 1.33.1

  • process >= seems not strictly needed and is no longer required, improving installability. #2149

  • print and close now 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- to avoid display problems in recent MS Terminal on Windows.

  • process >= 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 allow date: to be used within an OR expression, 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.


  • 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 special date and date2 tags, and the modified and _modified tags generated by --auto, are now also implicitly declared. #2148, #2119

  • Regular expression match group references in CSV if rules, 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 local if block. #2158 (Jonathan Dowland)

  • roi now correctly interacts with --value. #2190 (Dmitry Astapov)

  • hledger now requires process- to avoid any vulnerabilities on Windows from HSEC-2024-0003.


  • close has had some enhancements for usability (#2151):

    • It now excludes equity accounts by default; and always excludes the balancing account.

    • It has new --assert and --assign modes, for generating transactions which make balance assertions or balance assignments. There is also a --assertion-type option for changing the assertion/assignment type.

    • It adds a tag to generated transactions, named start, assert or retain depending on the mode.

    • The start tag'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 --migrate on 2024.journal will add the tag start:2025.journal to both transactions. Tags like this can be helpful when reading multiple files, for excluding closing and opening balances transactions (eg with not:tag:start=2025).

    • You can set different tag values by writing the mode option with an argument. Eg: hledger close --migrate=NEWFILENAME.

    • close now supports --round for controlling display of decimal places, like print.

    • examples/multi-year/ is examples/tutorial for managing multiple files with the close command.


  • stats has 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/--verbose flag.

    • 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_FILE are now respected. Eg, setting it to *.journal' or 2???.journal now 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: and ssv: file name prefixes are now supported in addition to csv:. 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)

  • print and close add 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.


  • add version annotations for features added in 1.32 (hamzashezad)
  • add Text encoding section, mention UTF-8 BOM support #2189
  • journal: note that payee and tag directives can't have tags in comments, unlike account.
  • 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


  • 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


  • move readFileStrictly to hledger-lib:Hledger.Utils.IO

hledger-ui 1.33


  • Require process to avoid any vulnerabilities on Windows from HSEC-2024-0003.


  • Add a dark theme. (Jonathan Dowland)


  • Allow building with GHC 9.8.

  • Require safe >=0.3.20.

hledger-web 1.33


  • Exclude base64 >=1.0 to avoid compilation failure. #2166

  • Preserve line breaks when showing an error message. #2163 (Martijn van der Ven)


  • 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 amount HTML class.

  • Allow building with GHC 9.8.

  • Require safe >=0.3.20.


  • Mention the -E/--empty flag for hiding zeros, the non-display of costs, and non-zeros that look like zero because of hidden costs.

project changes 1.33


  • Apple ARM binaries are now included in github releases.


  • 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


  • 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). #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


  • In CSV field assignments, %FIELD interpolation and \n can be used together again. #2134

  • In timedot data, numbers beginning with a decimal point are accepted again. #2130

  • In a balance --budget report, --layout=tall no 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.


  • Allow megaparsec 9.6


  • Updated: Queries, Periodic transactions, Auto postings, Assertions and costs, Budget report

hledger-ui 1.32.2


  • hledger-ui is now available on Windows (ShrykeWindgrace)


  • Use Notepad as default editor on Windows (ShrykeWindgrace)

  • Allow brick 2.2 (Vekhir)

  • Allow megaparsec 9.6

hledger-web 1.32.2


  • The --base-url option works again. #2127, #2100

  • Startup messages are more accurate and informative, eg with --socket. #2127

  • The non-working --file-url option has been dropped for now. #2139


  • 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 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: import with multiple files now updates .latest files correctly. (#2125)

  • Fixed: print --round=hard now 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 let hledger check payees accept payee-less transactions. (#2119)

  • Built-in tags with special meaning like type: and t: are now implicitly declared, so using type: in account declarations or generating t: with timedot letters won't cause hledger check tags to 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 prices or roi output), 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).


  • Timedot format supports a new letters syntax for easier tagged time logging. (#2116)

  • print has a new beancount output 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, is etc.) can use the new --summary-only flag (--summary also works) to display just the Total and Average columns (if enabled by --row-total and -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).


  • Journal format no longer fails to parse Ledger-style lot costs with spaces after the {, improving Ledger compatibility.

  • import now 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.

  • print now shows zeros with a commodity symbol and decimal digits when possible, preserving more information.

  • print has 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: print shows 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 printamtcostbalbalcost
  • The prices command 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-reverse and --infer-market-prices flags now combine properly.

    • --show-reverse now 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: or amt: now works properly.


  • print now styles balance assertion costs consistently, like other amounts.

  • import now works with -s/--strict. And more generally, when reading multiple input files, eg with multiple -f options, 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 .json or .sql file extension with -o/--outputfile now 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 demo command 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)


  • 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


  • 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


  • 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)


  • The --capabilities and --capabilities-header options have been replaced with an easier --allow=view|add|edit|sandstorm option. add is the default access level, while sandstorm is 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 adeclarationinfo field is now included in JSON output. (#2097) (S. Zeid)


  • The app can now serve on address (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


  • 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


  • New:

    • Fidelity CSV rules
  • Updated:

    • roi-unrealised.ledger (Charlie Ambrose)


  • 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 cli class instead of shell for command-line examples, avoiding inaccurate highlighting.


  • 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


  • 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


  • Multi-pivot: the --pivot option now accepts multiple arguments, colon-delimited, to construct account names from multiple fields. (#2050, Eric Mertens)


  • The print command 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.)

  • print no 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. Eg 1,000 (where the comma is a thousands separator) is now shown as 1,000..

  • The check command's balancedwithautoconversion and balancednoautoconversion checks have been renamed to autobalanced and balanced.

  • hledger check recentassertions now 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, desc is 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.


  • 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:

        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


  • Allow megaparsec 9.5

hledger-web 1.31


  • Allow aeson 2.2, megaparsec 9.5

project changes 1.31


  • ft, tt shell scripts for collecting financial and time reports

  • A justfile implementation of ft and tt


  • self-tracking

  • RPG ledger (Eric Mertens)



  • 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


  • Add missing files to Hackage release, making it buildable.


  • 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 skip count 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)


  • 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 FILE rule 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*.csv in foo-checking.csv.rules, periodically download CSV from Foo's website accepting your browser's defaults, and then run hledger import checking.csv.rules to 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)


  • Add-on commands can now have .js, .lua, or .php file 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-tags if you want them added.

  • We now try harder to ensure less (and its more mode) 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 --match mode 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 info program, as on mac. (#1770)


  • 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 month with --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.


  • Miscellaneous manual cleanups.

  • Rewrite introductory sections, Date adjustment, Directives, Forecasting, etc.

  • Add Paging section.

  • Remove archaic mentions of setenv.


  • Renamed: Hledger.Cli.Commands: findCommand -> findBuiltinCommand

hledger-ui 1.30


  • A "Cash accounts" screen has been added, showing accounts of the Cash type.


  • The top-level menu screen is now the default screen. Power users can use the --cash/--bs/--is/--all flags to start up in another screen.

  • "All accounts" screen has been moved to the bottom of the list.

  • Screens' help footers have been improved.


  • The transaction screen's inability to update is now noted.

  • Miscellaneous manual cleanups.

hledger-web 1.30


  • A command line depth limit now works properly. (#1763)


  • Miscellaneous manual cleanups.

project changes 1.30


  • hledger-bar: new script for making simple bar charts in the terminal

  • hledger-install: also list cabal, stack, pip tool versions


  • examples/csv: added a more up-to-date CSV makefile

  • examples/i18: Added sample top level account and type declarations in several languages


  • 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 close command has been continued. Here are all the changes to close since 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 --migrate flag.

    • The accounts closed by default are now just the ALE accounts (accounts declared or inferred as type Asset, Liability, or Equity). 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 Revenue or Expense), use the new --retain flag.

    • The equity command alias, removed in 1.29, has been restored.

    • The --open-acct option, removed in 1.29, has been restored.

    • The --closing and --opening flags have been renamed to --close and --open. (--close had 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.



  • type: queries now "see through" account aliases and pivots, as they did in hledger <1.27, and as acct: 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


  • A pager is used to show --help output when needed, as in hledger.


  • The corruption in 1.29's info manual is fixed. (#2023)

hledger-web 1.29.2


  • A pager is used to show --help output when needed, as in hledger.


  • The corruption in 1.29's info manual is fixed. (#2023)

project changes 1.29.2


  • 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


  • Hledger.Cli.Script now also exports


    and new string helpers

  • Allow building with GHC 9.6.1 (#2011)


  • The stats report no longer displays "Exact" in front of dates. (#2012)


  • remove duplicate in hledger close docs (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).


  • In journal format there is now a tag directive for declaring tag names, and the check command now has a tags check 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 treats inferred dates, and dates where the day is unspecified, as "flexible" (which can be automatically adjusted to interval boundaries), and dates specified to the day as "exact" (which can not). Eg:

    • A periodic rule like ~ monthly from 2023-01-15 now 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/15 now start the report on exactly 1/15 instead of being adjusted to 1/1.

    Note: periods using in may look partial but are considered to specify exact dates. So weekly reports such as -p 'weekly in 2023-01', which previously were adjusted to start on a monday, will now start exactly on 2023-01-01. This can also cause more verbose column headings. To guarantee simple week headings, you must now start such reports exactly 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/Conversion or name equity:conversion/equity:trade/equity:trading, or subaccounts of these. See also COST.)


  • 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 --version output 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 payee or 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 --match mode of the register command. (This command was used by ledger-autosync at one point; if you still need it, hopefully register --match works 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 --forecast transactions. (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-costs now works in transactions where an amount is left blank.

  • account declarations 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.

    • since is accepted as synonym of from in period expressions
    • apply year and year are accepted as synonyms of Y
    • (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 year are now ignored
    • subdirectives of payee, tag, and commodity (other than format) are now ignored
    • pop directive is no longer supported
  • When reading CSV, we now check that assigned account names are valid (parseable). (#1978)


  • aregister now handles an extra account query correctly. (#2007)

  • balance's --help now mentions --layout=tidy

  • Balance commands with --layout=bare now generate proper table layout in HTML output.

  • register's -w/--width option no longer gives ugly parse error messages.

  • stats's --help no 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 can now use Hledger.Cli.Script, a convenient new prelude which helps reduce import boilerplate. It currently re-exports:

    Text.Printf hiding (formatString)
    Data.Text (Text, pack, unpack)
    Safe hiding (at)
    Hledger.Cli.Main (argsToCliOpts)

    (Not much of Data.Text/Data.Text.IO because those need to be qualified.)


  • 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


  • 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


  • 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
  • CREDITS: updates, link to github contributors list


  • 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


  • The accounts command 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 -u and -d short flags have been added for --used and --declared.

  • A new CSV rule intra-day-reversed helps 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,

    1. When a CSV has date-times with an implicit timezone different from yours, you can use the timezone rule to declare it.

    2. CSV date-times with a known timezone (either declared by timezone or parsed with %Z) will be localised to the system timezone (or to the timezone set with the TZ environment variable).



  • 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.


  • In CSV rules, when assigning a parenthesised account name to accountN, extra whitespace is now ignored, allowing unbalanced postings to be detected correctly.


  • 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


  • 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 --all to replicate the old behaviour).


  • 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.


  • 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.


  • 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


  • --debug with no argument is now equivalent to --debug=1.

  • Allow megaparsec 9.3 (Felix Yan)

  • Support GHC 9.4

project changes 1.28


  • Miscellaneous improvements.


  • Indian National Pension Service CSV rules (Pranesh Prakash)


  • 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


  • Balance commands using -T -O html no 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


  • 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


  • 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)


  • 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.


  • 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.


  • 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


  • 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


  • 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


  • Respect the add form's file selector again. (Simon Michael, Kerstin, #1229)

project changes 1.27


  • 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)


  • Add example for capital one credit cards CSV. (max thomas)


  • 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


  • register and aregister have 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-all flag. (#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 check command 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 ordereddates now always checks all transactions (previously it could be restricted by query arguments).

  • The --pivot option now supports a status argument, to pivot on transaction status.

  • Update bash completions (Jakob Schöttl)


  • Value reports with --date2 and a report interval (like hledger 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 %999 or %nosuchfield is 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-price was inferring a negative price when selling. (#1813, Stephen Morgan)

  • Allow an escaped forward slash in regular expression account aliases. (#982, Stephen Morgan)

  • The tags command now also lists tags from unused account declarations. It also has improved command-line help layout. (#1857)

  • hledger accounts now shows its debug output at a more appropriate level (4).

hledger-ui 1.26

  • Uses hledger 1.26.

hledger-web 1.26


  • Don't add link URLs when printing.


  • Now builds with GHC 9.2.

  • Uses hledger 1.26.

project changes 1.26


  • 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)


  • 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.)


  • invoice: calculate dates accurately on last days of month


  • 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, replacing push and pull. 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-test on another github branch first.
    • Mac and Windows binaries are now stripped also (if applicable).
  • make buildtimes, make buildtimes-cabal show 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 TYPECODE syntax, deprecated in 1.13, has been dropped. Please use account NAME ; type:TYPECODE instead. (Stephen Morgan)

  • The rule for auto-detecting "cash" (liquid asset) accounts in the cashflow report has changed: it's now "all accounts under a top-level asset account, with cash, bank, checking or saving in their name" (case insensitive, variations allowed). So if you see a change in your cashflow reports, you might need to add account directives with type:C tags, declaring your top-most cash accounts.


  • The new type:TYPECODES query matches accounts by their accounting type. Account types are declared with a type: 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 of balancesheet, incomestatement etc., so you can now select accounts by type without needing fragile account name regexps. Also, the accounts command has a new --types flag 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

    (#1820, #1822) (Simon Michael, Stephen Morgan)

  • 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-equity flag 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:

      a             1 AAA @@ 2 BBB
      b            -2 BBB
    $ hledger print --infer-equity
      a                               1 AAA
      equity:conversion:AAA-BBB:AAA  -1 AAA
      equity:conversion:AAA-BBB:BBB   2 BBB
      b                              -2 BBB

    equity:conversion is the account used by default. To use a different account, declare it with an account directive and the new V (Conversion) account type. Eg:

    account Equity:Trading    ; type:V

    (#1554) (Stephen Morgan, Simon Michael)

  • Balance commands (bal, bs etc.) 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)


  • Strict mode (-s/--strict) now also checks periodic transactions (--forecast) and auto postings (--auto). (#1810) (Stephen Morgan)

  • hledger check commodities now 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 days
    • in -6 months
    • 5 weeks ahead
    • 2 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")


  • --layout=bare no longer shows a commodity symbol for zero amounts. (#1789) (Stephen Morgan)

  • balance --budget no longer elides boring parents of unbudgeted accounts if they have a budget. (#1800) (Stephen Morgan)

  • roi now 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)


hledger-ui 1.25

  • Uses hledger 1.25.

hledger-web 1.25

  • Uses hledger 1.25.

project changes 1.25


  • 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


  • The RELEASING doc and release process has been updated, and a new helper script added: tools/releaseprep. make hackageupload now 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-ui now 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


  • balance --declared is now filtered correctly by a not:ACCT query. (#1783)

  • More reliable --version output, with commit date and without patch level.

hledger-ui 1.24.1


  • 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


  • 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


  • 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 --declared flag, 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-mark directive as a more principled way (than commodity directives) to specify the decimal character in use in that file, to ensure accurate number parsing. (#1670, Lawrence Wu)


  • 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)


  • 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


  • 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.


  • The Z key for toggling display of zeroes is now the easier lower-case z.

  • The --watch feature now has a convenient short flag, -w.

  • Drop the base-compat-batteries dependency. (Stephen Morgan)

  • Allow megaparsec 9.2


  • 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


  • Allow megaparsec 9.2

project changes 1.24


  • bin/hledger-check-fancyassertions.hs: fix ugly assertion parse errors. (ShrykeWindgrace)

  • bin/hledger-check-tagfiles.hs: Update description, clarify wording. (Pranesh Prakash)


  • 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)


  • 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


  • 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)


  • 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)


  • make site-watch works again

  • make list-commits and make showauthors show those things.

  • Shake cabalfiles now 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


  • The balance command has a new --gain report 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-style option 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-column flag 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 --budget option 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 weekendday and every mon,wed,... (multiple days of the week). This is intended for periodic transaction rules used with --forecast (or bal --budget). (#1632, Lawrence Wu)

  • The new --today=DATE option 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 \n when assigning to comment, comment1 etc. (Malte Brandy)


  • Incremental performance improvements; hledger 1.23 is the fastest hledger yet, about 10% faster than 1.22. (Stephen Morgan)

  • register no 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)

  • --sort now 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-price has 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-amount flag 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 --forecast argument, 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 --forecast argument, 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-01 on 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)

  • 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..)


  • 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 roi command no longer gives an ugly error in a certain case with PnL applied on the first day of investment. (Dmitry Astapov)

  • --forecast now 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


  • Depend on hledger 1.23.

  • Require base >=4.11, prevent red squares on Hackage's build matrix.

hledger-web 1.23


  • Drop the obsolete hidden --binary-filename flag.

  • Depend on hledger 1.23.

  • Require base >=4.11, preventing red squares on Hackage's build matrix.


  • 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 tomorrow or date:-tomorrow in the command, as with other reports. (Stephen Morgan)


  • Timedot format's doc has been rewritten.


  • 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


  • 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


  • cur: and amt: 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


  • Document watch mode and its limitations. (#1617, #911, #836)

  • Allow megaparsec 9.1.


  • 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 V key 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


  • deps: Allow megaparsec 9.1.


  • 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


  • 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.


  • The info manuals now have the proper metadata so you or your packager can install them with install-info and they will appear in info's Directory. We also provide a dir file making it easy for developers to see the latest dev manuals in their info Directory. (#1585) (Damien Cassou, Simon Michael)


  • 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


  • check: A new balancednoautoconversion check requires transactions to balance without the use of inferred transaction prices. (Explicit transaction prices are allowed.) This check is included in --strict mode. The old autobalanced check has been renamed to balancedwithautoconversion. (Stephen Morgan)


  • 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-file option 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)


  • Auto posting rules now match postings more precisely, respecting cur: and amt: 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)

  • 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 print shows 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


  • Don't reset the B/V (cost, value) state when reloading with g or --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 --version flag 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)


  • Queries in the register screen work again (broken in 1.21). (#1523) (Stephen Morgan)

  • Don't write to ./debug.log when toggling value with V, or when reloading with g or --watch in the Transaction screen. (#1556) (Simon Michael, Stephen Morgan)

hledger-web 1.22


  • 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.


  • 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


  • 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/-h and --version flags are no longer position-sensitive; if there is a command argument, they now always refer to the command (where applicable).

  • The new --info flag opens the hledger info manual, if "info" is in $PATH. hledger COMMAND --info will open COMMAND's info node.

  • The --man flag opens the hledger man page, if "man" is in $PATH. hledger COMMAND --man will 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 aliases a, b, p, and r. 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 balance command docs and "commands" section have been rewritten.


  • Costing and valuation are now independent, and can be combined. --value=cost and --value=cost,COMM are still supported (equivalent to --cost and --cost --value=then,COMM respectively), but deprecated. (Stephen Morgan)

  • -V is now always equivalent to --value=end. (Stephen Morgan)

  • --value=end now includes market price directives as well as transactions when choosing a valuation date for single-period reports. (#1405, Stephen Morgan)

  • --value=end now picks a consistent valuation date for single- and and multi-period reports. (#1424, Stephen Morgan)

  • --value=then is now supported with all reports, not just register. (Stephen Morgan)

  • The too-vague --infer-value flag has been renamed to --infer-market-price. Tip: typing --infer-market or even --infer is sufficient. The old spelling still works, but is now deprecated.


  • 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: --debug now shows transaction matching results, useful when troubleshooting.

  • balance: To accomodate new report types, the --change|--cumulative|--historical|--budget flags have been split into two groups: report type (--sum|--budget|...) and accumulation type (--change|--cumulative|--historical). --sum and --change are the defaults, and your balance commands should still work as before. (Stephen Morgan et al, #1353)

  • balance: The --valuechange report 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 with balancesheet etc. (#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
    • accounts and commodities may also be specified as arguments
    • ordereddates now checks each file separately (#1493)
    • ordereddates no longer supports the --unique flag or query arguments
    • payees is a new check requiring payee declarations
    • uniqueleafnames now gives a fancy error message like the others
    • the old checkdates/checkdupes commands have been dropped
  • help: The help command now shows only the hledger (CLI) manual, its --info/--man/--pager flags have been renamed to -i/-m/-p, and --cat has 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 --man and --info flags described above.)

  • payees: Add --used/--declared flags, like the accounts command.

  • 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: --match now provides debug output useful for troubleshooting.

    If you forget to give --match an argument, it can confusingly consume a following flag. Eg if you write:

    hledger print --match -x somebank   # should be: hledger print --match=somebank -x

    it 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 commodity directive 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 --man and --info open 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 --man and --info open 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=1 or --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 --drop option now works with csv and html output. (#1456) (Ilya Konovalov)

  • check: the commodities check, and -s/--strict mode, 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 commodity directive, 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


  • 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)
  • 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)


  • 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-mark rule 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)

  • E now parses the HLEDGER_UI_EDITOR or EDITOR environment 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.

  • E now supports positioning when HLEDGER_UI_EDITOR or EDITOR is 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


  • 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 sql output format has been added (Dmitry Astapov)

  • A --color/--colour command line option, support for the NO_COLOR environment 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 2020q1 or Q4. (#1247, Henning Thieleman, Stephen Morgan)

  • When specifying report intervals, you can use fortnightly as a synonym for biweekly. (Stephen Morgan)


  • 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:

    1. normal command output only (no warnings)
    2. useful warnings & most common troubleshooting info (valuation, eg)
    3. common troubleshooting info, more detail
    4. report options selection
    5. report generation
    6. report generation, more detail
    7. input file reading
    8. input file reading, more detail
    9. command line parsing
    10. any other rarely needed or more in-depth info


  • 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, not 10/02/01. would need to be written 0200/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 the cashflow report. Cash accounts are also Asset accounts.

  • 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 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 amount and amount1), we ignore the unnumbered ones. This makes it easier to override old amount rules.

  • 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 from hledger to hledger-ui lost the quotes around amt:>20 and 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)


  • 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-acct

    The 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 to commodity directives 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 separator directive in CSV rule files. (Aleksandar Dimitrov)

  • The .tsv and .ssv file extensions are now recognised, and will set the default separator to 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 --anon flag now also anonymises transaction codes and account names declared with account directives. (Mykola Orliuk) (#901)

  • The benchmark suite has been disabled.


  • balance/bs/cf/is: balance commands now support the -%/--percent flag 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 --tree has 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: --budget no longer errors when there is neither budget nor transactions in the report period (Dmitry Astapov)

  • balance: --budget has 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-from options allow closing/opening account names to be chosen

  • import: create the journal if missing, like the add command Streamlines import/migration instructions.

  • import: --catchup marks 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, skip can be used to skip one or more records after a pattern match, or the new end rule can be used to skip all remaining records. (#1076)

  • The new balance-type CSV 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 -B and -V command line flags)

  • uses hledger 1.16.1

hledger-web 1.16

  • The --cors option 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=COMM variants. 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 -M and -p interact has been updated and clarified. (Jakob Schöttl) (#1008, #1009, #1011)

  • Restore --aux-date and --effective as --date2 aliases (#1034). These Ledger-ish spellings were dropped over the years, to improve --help's layout. Now we support them again, as semi-hidden flags (--help doesn't list them, but they are mentioned in --date2's help).


  • 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/--historical or --cumulative now disables -T/--row-total (#329). Multiperiod balance reports which show end balances (eg, bal -MH or bs -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 -B report 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 --values flag 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, and modified tags, 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-api disables 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 -h as 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 --invert option 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:


    And allow adding a new transaction by PUT'ing JSON (similar to the output of /transactions) to /add. This requires the add capability (which is enabled by default). Here's how to test with curl:

    $ curl -s -X PUT -H 'Content-Type: application/json' --data-binary @in.json; echo


  • fix unbalanced transaction prevention in the add form

  • fix transaction-showing tooltips (#927)

  • manual updates: document --capabilities/--capabilities-header and 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).


  • 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-dry

    update 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-budgeted flag has been dropped. (Dmitry Astapov)

  • balance: new --transpose flag 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 is Asset, Liability, Equity, Revenue, Expense, A, L, E, R or X (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 (--auto can 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


    • 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)

  • --anon anonymises more thoroughly (including linked original postings) (Moritz Kiefer)

  • unbalanced transaction errors now include location info (Mykola Orliuk)

  • accounts command: --drop also affects the default flat output, without needing an explicit --flat flag

  • accounts command: the --codes flag 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:

      (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-toggles flag

  • periodic transactions and transaction modifiers are always enabled.
    Rule-based transactions and postings are always generated (--forecast and --auto are always on). Experimental.

  • escape key resets to flat mode.
    Flat mode is the default at startup. Probably it should reset to tree mode if --tree was used at startup.

  • tree mode tweaks: add --tree/-T/-F flags, make flat mode the default,
    toggle tree mode with T, ensure a visible effect on register screen

  • hide future txns by default, add --future flag, toggle with F.
    You may have transactions dated later than today, perhaps piped from print --forecast or 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. --present and --future flags 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)
    The journalFinalise function 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/--closing flags 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 --slow to 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: alias and apply account directives now affect account directives (#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: --drop is now ignored when not in flat mode, rather than producing a corrupted report (#754)

  • budget: --drop now preserves the top-level account in --budget reports

  • 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 --anon flags is now preserved on reload (#753)

  • edit-at-transaction-position is now also supported when $EDITOR is neovim

  • support/require fsnotify

  • 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 -f options, 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.

  • journal: support scientific number notation (#704, #706)

  • 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 --declared and --used flags

  • bal: the --invert flag flips all signs

  • bal: --drop now 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 --budget is unimplemented

  • budget: bal --budget shows 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: --budget uses 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/--empty toggles zeroes at startup (with opposite default to cli)

hledger-web 1.9

  • -E/--empty toggles zeroes at startup (with opposite default to cli)

hledger-api 1.9

2017/12/31 hledger 1.5


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/--value uses 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 --auto flag (Dmitry Astapov)

  • journal: support Ledger-style periodic transactions, enabled with --forecast flag (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 th day of week" (Dmitry Astapov)

hledger cli 1.5

  • --auto adds Ledger-style automated postings to transactions (Dmitry Astapov, Mykola Orliuk)

  • --forecast generates Ledger-style periodic transactions in the future (Dmitry Astapov, Mykola Orliuk)

  • -V/--value uses 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: --budget shows 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-run is now valid journal format

  • print: -B shows 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 -H showing nothing for empty periods (#583, Nicholas Niro) This patch fixes a bug that happened when using the -H option 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 -H with -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 -NUM as a shortcut for --depth=NUM (eg: -2)

  • cli: improve command-line help for --date2 (#604)

  • cli: make --help and -h the same, drop --man and --info for 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/--cat flags. (#579)
  • bal/bs/cf/is: --sort-amount/-S sorts by largest amount instead of account name

  • bs/cf/is: support --output-file and --output-format=txt|csv The 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: --new shows 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 --pivot and --anon options, like hledger CLI (#474) (Jakub Zárybnický)

  • accept -NUM as 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 --pivot and --anon options, like hledger CLI (#474) (Jakub Zárybnický)

  • web: Make "Add transaction" button tabbable (#430) (Jakub Zárybnický)

  • accept -NUM as 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.


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)


Multiple status: query terms are now OR'd together. (#564)

Deps: allow megaparsec 5.3.

hledger cli 1.3


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.


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


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).


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.


consolidate extra/ and data/ in examples/, tarsnap csv rules & reporting example, xpensetracker csv rules.


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.


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


"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


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


A new --pretty-tables option uses unicode characters for rendering table borders in multicolumn reports (#522) (Moritz Kiefer)


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)


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


  • 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 *.hledger files 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.


  • 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


  • with -V, don't ignore market prices in the future (#453, #403)

  • with -V and multiple same-date market prices, use the last parsed not the highest price (#403)


  • 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 changes

    hledger-ui --watch will 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 --change flag shows period changes at startup instead of historical ending balances

  • the A key runs the hledger-iadd tool, if installed

  • always reload when g is pressed

    Previously 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

  • add --host option (#429)

    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 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 --server to --serve

    The --server flag sounded too close in meaning to --host so 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 by default, --host option added (#432)

    Consistent with hledger-web: serves only local requests by default, use --host=IPADDR to 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.


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.


  • 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


  • 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)


  • parsing multiple input files is now robust.

    When multiple -f options 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 (like Y or alias) no longer carry over from one file to the next.

  • -I has 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 --debug option 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.


  • 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 --historical flag. 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


  • suggest only one commodity at a time as default amount (#383)

    (since we currently can't input more than one at a time)


  • added --change flag for consistency

  • -H/--historical now 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)


  • fix an infinite loop (#393)
  • in CSV output, fix and rename the transaction id field


  • fix a sorting regression with --date2 (#326)

  • --average/-A is now affected by --historical/-H

  • added --cumulative flag for consistency

  • in CSV output, include the transaction id and rename the total field (#391)


  • fixed an issue with ordering of include files


  • --pivot option added, groups postings by tag instead of account (#323) (Malte Brandy)

  • --anon option 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/--historical flags, 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-elide for 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:

    1. exclude transactions which affect the current account via an excluded posting type. Eg when --real is in effect, a transaction posting to the current account with only virtual postings will not appear in the report.

    2. 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.


  • 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/--empty is 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: arg or --date2 flag might also affect it (untested).

  • hledger-ui now uses the quicker-building microlens

hledger-web 1.0


  • 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)


  • 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


  • 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.


  • 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 --depth or depth: must now be positive.


  • 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.


  • There is now a -V/--value flag 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 -V uses only the market prices recorded with P directives; it does not use the transaction prices recorded as part of posting amounts. Using both -B and -V at 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 --format option 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.


  • The --match option 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.)


  • 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 --flat toggle
    • allows --cleared toggle
    • 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-url option (#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 comment directive 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.


  • 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 --empty flag 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 --pending flag 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 --uncleared and status: as "any state except cleared", it's not currently possible to match things which are neither cleared nor pending.


  • activity no longer excludes 0-amount postings by default.


  • Don't show quotes around the journal file path in the "Creating..." message, for consistency with the subsequent "Adding..." message.


  • Accounts beginning with "debt" or now also recognised as liabilities.


  • 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
        c    C 10.00
        c    C 11.00
        d  D -320.00
        c  C 10.00 @ D 15.2381
        c  C 11.00 @ D 15.2381
        d     D -320.00

    There 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.


  • Postings with no amounts could give a runtime error in some obscure case, now fixed.


  • 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 |


  • 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:

    1. a --width option
    2. or a COLUMNS environment variable (NB: not the same as a bash shell var)
    3. or on POSIX (non-windows) systems, the current terminal width
    4. 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 ....

  • register's --width option 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
    $ 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-total and -A/--average options

    In multicolumn balance reports, -T/--row-total now shows a totals column and -A/--average shows an averages column. This helps eg to see monthly average expenses (hledger bal ^expenses -MA).

    NB our use of -T deviates from Ledger's UI, where -T sets a custom final total expression.

  • balance: -N is now short for --no-total

  • balance: fix partially-visible totals row with --no-total

    A periodic (not using --cumulative or --historical) balance report with --no-total now 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:


  • 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.


  • the --width and --debug options 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-file and -O/--output-format options. 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


  • 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)


  • fix: in tree mode, --drop is 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, -E now 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


  • 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/--average now implies -E/--empty
  • postings with multi-commodity amounts are now top-aligned, like Ledger

User-visible changes in hledger-web since 0.23.3:


  • 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


  • 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).


  • 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.


  • An accounts command has been added, similar to Ledger's, for listing account names in flat or hierarchical mode.


  • 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 reports in flat mode now always show exclusive (subaccount-excluding) balances.
  • Balance reports in flat mode with --depth now 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 --flat and --drop.


  • Tag queries (tag:) will now match a transaction if any of its postings match.


  • The --display option 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: --date2 now works with report intervals (fixes #174).


  • 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: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.


  • Default report dates now derive from the secondary dates when --date2 is 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-root flag 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


  • web: new option --static-root to set the base url for static files


  • 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



  • 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:REGEXP matches commodity symbols

  • register: --average/-A shows a running average, like ledger

  • in period expressions, - (hyphen) can be used as a more compact synonym for from and to. Eg: -p 2012/12/1-2013/2/1 or date: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 names
    • hledger-balance-csv.hs - print a balance report as CSV
    • hledger-equity.hs - print an entry matching all account balances (like ledger)
    • hledger-print-unique.hs - print only journal entries unique descriptions
    • hledger-register-csv.hs - print a register report as CSV


  • balancesheet: now shows just assets and liabilities, not equity

  • print: comment positions (same line or next line) are now preserved

  • queries: amt now uses the = operator by default, eg amt:50 is equivalent to amt:=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.

  • --debug now takes an optional numeric argument to set the debug level higher than 1, for more verbose debug output in a few cases.


  • 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 --constraint to 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
  • 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 --flat has been fixed (#94)
  • register: when --date2 is 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 --date2 to select the secondary date for reports. (--aux-date or --effective are 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.rules instead of FILE.rules
    • FIELD-field N is now FIELD %N+1 (or set them all at once with a fields rule)
    • base-currency is now currency
    • base-account is now account1
    • account-assigning rules: add if before the list of regexps, add indented account2 before the account name
  • parenthesised amounts are parsed as negative


  • Use code: to match the transaction code (check number) field
  • Use amt: followed by <, = or > and a number N to match amounts by magnitude. Eg amt:<0 or amt:=100. This works only with single-commodity amounts (multi-commodity amounts are always matched).
  • tag: can now match (exact, case sensitive) tag values. Eg tag: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/-r flag shows the other postings in each transaction, like ledger.
  • The --width/-w option 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_FILE or ~/.hledger.journal
  • the blaze_html_0_5 build flag has been reversed and renamed to blaze_html_0_4


  • 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 (--related and --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 --depth properly 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 devel work
  • 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, and cashflow provide 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.csv just do hledger -f FILE.csv print or any other command. You can also pipe any supported format into hledger -f- CMD and 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/--cost now 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 --version now 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 --empty and 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 --help to 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 -f and 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 aliases directives, for renaming accounts, are supported, like ledger's but a bit more powerful; also an --alias option for renaming on the fly
  • parsing: the account directive now preserves posting type (normal/virtual/balanced virtual)
  • parsing: the pop directive is supported as an alias for end 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-field can be used when the CSV file specifies both accounts
  • convert: description-field can have a custom format and combine multiple CSV fields
  • convert: in-field and out-field support 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 -s flag 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/--daily flag
  • 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 --version output

2010/07/17 hledger 0.11


  • split --help, adding --help-options and --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: --flat provides a simple non-hierarchical format
  • balance: --drop removes leading account name components from a --flat report
  • 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/--period support; 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: --port and --base-url options 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 --version output
  • 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-accounts option 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.
  • --effective option, will use transactions' effective dates if any
  • convert: new rules file format, find/create rules file automatically, more robust parsing, more useful --debug output
  • 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
  • --debug now 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/-Q option summarises by quarter
  • --uncleared/-U option 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-anywhere flag, that is now the default
  • patterns now use not: and desc: prefixes instead of ^ and ^^
  • patterns are now case-insensitive, like ledger
  • !include directives 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 -E is used
  • balance report hides the final total when --no-total is used
  • --depth affects print and register reports (aggregating with a reporting interval, filtering otherwise)
  • register report sorts transactions by date
  • register report shows zero-amount transactions when -E is 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 --verbose output
  • --version gives 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/-p flag accepting period expressions like "in 2008", "weekly from last month"..
  • -W/-M/-Y convenience flags to summarise register weekly, monthly, yearly
  • --depth and -E flags also affect summarised register reports (including depth=0)
  • --display/-d flag supporting date predicates (like "d<[DATE]", "d>=[DATE]")
  • !include directive to include additional ledger files
  • !account directive 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 --showsubs to --subtotal, like ledger
  • drop --usage flag
  • don't require quickcheck
  • priced amounts (eg "10h @ $50") and --basis/--cost/-B flag to show them with cost basis
  • easy --depth option, equivalent to ledger's -d 'l<=N'
  • smarter y/m/d date parsing for -b and -e (any number of digits, month and day default to 1, separator can be / - or .)
  • -n flag for balance command
  • --empty/-E flag
  • 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/-R flag
  • support -C/--cleared flag 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/-v flag, 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

Get Started

Starting out with hledger and Plain Text Accounting, not to mention setting up a new accounting system, can be a lot to take in. This page aims to help! After installing hledger, reading some of the docs below should be helpful.

Quick starts

We have (too many) quick introductions. These assume a little bit of command line know-how:


Detailed step by step tutorials, with screenshots:



The manuals are hledger's authoritative documentation, and the most maintained and accurate of the docs. Read them to know exactly what hledger does.

You can also view the hledger manual in the terminal with hledger help.


Practical advice and examples for real-world tasks:

See also:


  • See the Support page, especially the #hledger chat and hledger mail list


Here are some thoughts on how to approach hledger and accounting.

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. Prioritise your work: a good way is to think about your most pressing needs and what kind of report would help. For example,

  • Take inventory of your debts, loans and assets; write down the names and numbers.
  • Record these as "opening balances" transactions (as in the quick start docs).
  • Make corrections until hledger shows your balances 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 manually download your bank transactions as CSV and develop CSV rules so that you can print the CSV as journal entries.
  • Then try downloading and importing this CSV into your journal daily for a while. (Only if you wish. Many people stick to manual data entry for the increased awareness it brings.)

If the task feels unclear or overwhelming, I recommend this small steps, verifiable reports approach.

If not, of course feel free to blaze away and do it all on day one. But I would still recommend establishing a frequent reconciling routine. It is surprising how quickly small events can slip through the cracks and create chaos, and it takes a little time to develop the troubleshooting skills. Reconciling often will save you time.


Your bookkeeping does not have to be perfect or even very accurate [1]. 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.

[1] Though if you really catch the PTA bug, you may find that nothing less than perfection will do!



hledger - robust, friendly plain text accounting (CLI version)


hledger ADDONCMD -- [OPTS] [ARGS]


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.33. 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 get it from hledger itself with
hledger --man, hledger --info or hledger help [TOPIC].

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

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.



hledger reads one or more data files, each time you run it. You can specify a file with -f, like so

$ hledger -f FILE 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

Data files containing non-ascii characters must use UTF-8 encoding. An optional byte order mark (BOM) is allowed, at the beginning of the file (only).

Also, your system should be configured with a locale that can decode UTF-8 text. On some unix systems, you may need set the LANG environment variable, eg. You can read more about this in Unicode characters, below.

On unix systems you can check a file's encoding with the file command. If you need to import from a UTF-16-encoded CSV file, say, you can convert it to UTF-8 with the iconv command.

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:
journalhledger journal files and some Ledger journals, for transactions.journal .j .hledger .ledger
timeclocktimeclock files, for precise time logging.timeclock
timedottimedot files, for approximate time logging.timedot
csvComma or other character separated values, for data import.csv
ssvSemicolon separated values.ssv
tsvTab separated values.tsv
rulesCSV/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 add a file format prefix, like:

$ 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:

You can use the check command to run individual checks -- the ones listed above and some more.


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.

To show the commands list, run hledger with no arguments. The commands are described in detail in 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: programs or scripts named "hledger-SOMETHING", which will also appear in hledger's commands list. If you used the hledger-install script, you will have several add-ons installed already. Some more can be found in hledger's bin/ directory, documented at https://hledger.org/scripts.html.

More precisely, 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 using hledger, much like built-in commands: hledger ADDONCMD [-- ADDONCMDOPTS] [ADDONCMDARGS]. But note the double hyphen argument, required before add-on-specific options. Eg: hledger ui -- --watch or hledger web -- --serve. If this causes difficulty, you can always run the add-on directly, without using hledger: hledger-ui --watch or hledger-web --serve.


Run hledger -h to see general command line help, and general options which are common to most hledger commands. These options can be written anywhere on the command line. They can be grouped into help, input, and reporting options:

General help options

-h --help : show general or COMMAND help

--man : show general or COMMAND user manual with man

--info : show general or COMMAND user manual with info

--version : show general or ADDONCMD version

--debug[=N] : show debug output (levels 1-9, default: 1)

General input options

-f FILE --file=FILE : use a different input file. For stdin, use - (default: $LEDGER_FILE or $HOME/.hledger.journal)

--rules-file=RULESFILE : Conversion rules file to use when reading CSV (default: FILE.rules)

--separator=CHAR : Field separator to expect when reading CSV (default: ',')

--alias=OLD=NEW : rename accounts named OLD to NEW

--pivot FIELDNAME : use some other field or tag for the account name

-I --ignore-assertions : disable balance assertion checks (note: does not disable balance assignments)

-s --strict : do extra error checking (check that all posted accounts are declared)

General reporting options

-b --begin=DATE : include postings/txns on or after this date (will be adjusted to preceding subperiod start when using a report interval)

-e --end=DATE : include postings/txns before this date (will be adjusted to following subperiod end when using a report interval)

-D --daily : multiperiod/multicolumn report by day

-W --weekly : multiperiod/multicolumn report by week

-M --monthly : multiperiod/multicolumn report by month

-Q --quarterly : multiperiod/multicolumn report by quarter

-Y --yearly : multiperiod/multicolumn report by year

-p --period=PERIODEXP : set start date, end date, and/or reporting interval all at once using period expressions syntax

--date2 : match the secondary date instead (see command help for other effects)

--today=DATE : override today's date (affects relative smart dates, for tests/examples)

-U --unmarked : include only unmarked postings/txns (can combine with -P or -C)

-P --pending : include only pending postings/txns

-C --cleared : include only cleared postings/txns

-R --real : include only non-virtual postings

-NUM --depth=NUM : hide/aggregate accounts or postings more than NUM levels deep

-E --empty : show items with zero amount, normally hidden (and vice-versa in hledger-ui/hledger-web)

-B --cost : convert amounts to their cost/selling amount at transaction time

-V --market : convert amounts to their market value in default valuation commodities

-X --exchange=COMM : convert amounts to their market value in commodity COMM

--value : convert amounts to cost or market value, more flexibly than -B/-V/-X

--infer-equity : infer conversion equity postings from costs

--infer-costs : infer costs from conversion equity postings

--infer-market-prices : use costs as additional market prices, as if they were P directives

--forecast : generate transactions from periodic rules, : between the latest recorded txn and 6 months from today, : or during the specified PERIOD (= is required). : Auto posting rules will be applied to these transactions as well. : Also, in hledger-ui make future-dated transactions visible.

--auto : generate extra postings by applying auto posting rules to all txns (not just forecast txns)

--verbose-tags : add visible tags indicating transactions or postings which have been generated/modified

--commodity-style : Override the commodity style in the output for the specified commodity. For example 'EUR1.000,00'.

--color=WHEN (or --colour=WHEN) : Should color-supporting commands use ANSI color codes in text output. : 'auto' (default): whenever stdout seems to be a color-supporting terminal. : 'always' or 'yes': always, useful eg when piping output into 'less -R'. : 'never' or 'no': never. : A NO_COLOR environment variable overrides this.

--pretty[=WHEN] : Show prettier output, e.g. using unicode box-drawing characters. : Accepts 'yes' (the default) or 'no' ('y', 'n', 'always', 'never' also work). : If you provide an argument you must use '=', e.g. '--pretty=yes'.

When a reporting option appears more than once in the command line, the last one takes precedence.

Some reporting options can also be written as query arguments.

Command line tips

Here are some details useful to know about for hledger command lines (and elsewhere). Feel free to skip this section until you need it.

Option repetition

If options are repeated in a command line, hledger will generally use the last (right-most) occurence.

Special characters

Single escaping (shell metacharacters)

In shell command lines, characters significant to your shell - such as spaces, <, >, (, ), |, $ and \ - should be "shell-escaped" if you want hledger to see them. This is done by enclosing them in single or double quotes, or by writing a backslash before them. Eg to match an account name containing a space:

$ hledger register 'credit card'


$ hledger register credit\ card

Windows users should keep in mind that cmd treats single quote as a regular character, so you should be using double quotes exclusively. PowerShell treats both single and double quotes as quotes.

Double escaping (regular expression metacharacters)

Characters significant in regular expressions (described below) - such as ., ^, $, [, ], (, ), |, and \ - may need to be "regex-escaped" if you don't want them to be interpreted by hledger's regular expression engine. This is done by writing backslashes before them, but since backslash is typically also a shell metacharacter, both shell-escaping and regex-escaping will be needed. Eg to match a literal $ sign while using the bash shell:

$ hledger balance cur:'\$'


$ hledger balance cur:\\$

Triple escaping (for add-on commands)

When you use hledger to run an external add-on command (described below), one level of shell-escaping is lost from any options or arguments intended for by the add-on command, so those need an extra level of shell-escaping. Eg to match a literal $ sign while using the bash shell and running an add-on command (ui):

$ hledger ui cur:'\\$'


$ hledger ui cur:\\\\$

If you wondered why four backslashes, perhaps this helps:


Or, you can avoid the extra escaping by running the add-on executable directly:

$ hledger-ui cur:\\$

Less escaping

Options and arguments are sometimes used in places other than the shell command line, where shell-escaping is not needed, so there you should use one less level of escaping. Those places include:

  • an @argumentfile
  • hledger-ui's filter field
  • hledger-web's search form
  • GHCI's prompt (used by developers).

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, and it must be one that can decode the characters being used. In bash, you can set a locale like this: export LANG=en_US.UTF-8. There are some more details in Troubleshooting. This step is essential - without it, hledger will quit on encountering a non-ascii character (as with all GHC-compiled programs).

  • your terminal software (eg Terminal.app, iTerm, CMD.exe, xterm..) must support unicode

  • 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:

  1. they are case insensitive
  2. they are infix matching (they do not need to match the entire thing being matched)
  3. they are POSIX ERE (extended regular expressions)
  4. they also support GNU word boundaries (\b, \B, \<, \>)
  5. 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 digit 1.
  6. they do not support mode modifiers ((?s)), character classes (\w, \d), or anything else not mentioned above.

Some things to note:

  • In the alias directive and --alias option, 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, write cur:\$.

  • 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 reuse them by writing @FILENAME as a command line argument. Eg: hledger bal @foo.args.

Inside the argument file, each line should contain just one option or argument. Don't use spaces except inside quotes (or you'll see a confusing error); write = (or nothing) between a flag and its argument. For the special characters mentioned above, use one less level of quoting than you would at the command prompt.


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:

balanceY 1Y 1Y 1,2Y
balancesheetY 1Y 1Y 1Y
balancesheetequityY 1Y 1Y 1Y
cashflowY 1Y 1Y 1Y
incomestatementY 1Y 1Y 1Y
  • 1 Also affected by the balance commands' --layout option.
  • 2 balance does not support html output without a report interval or with --budget.

The output format is selected by the -O/--output-format=FMT option:

$ hledger print -O csv    # print CSV on stdout

or by the filename extension of an output file specified 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

Some notes about the various output formats:

CSV output

  • In CSV output, digit group marks (such as thousands separators) are disabled automatically.

HTML output

  • HTML output can be styled by an optional hledger.css file in the same directory.

JSON output

  • hledger represents quantities as Decimal values storing up to 255 significant digits, eg for repeating decimals. Such numbers can arise in practice (from automatically-calculated transaction prices), and would break most JSON consumers. So in JSON, we show quantities as simple Numbers with at most 10 decimal places. We don't limit the number of integer digits, but that part is under your control. We hope this approach will not cause problems in practice; if you find otherwise, please let us know. (Cf #1195)

SQL output

  • This is not yet much used; real-world feedback is welcome.

  • SQL output is expected to work at least with SQLite, MySQL and Postgres.

  • For SQLite, it will be 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' | ...
  • SQL output is structured with the expectations that statements will be executed in the empty database. If you already have tables created via SQL output of hledger, you would probably want to either clear tables of existing data (via delete or truncate SQL statements) or drop tables completely as otherwise your postings will be duped.

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 repeated to set the display style for multiple commodities/currencies. Its argument is as described in the commodity directive.

In some cases hledger will adjust number formatting to improve their parseability (such as adding trailing decimal marks when needed).


In terminal output, some commands can produce colour when the terminal supports it:

  • if the --color/--colour option is given a value of yes or always (or no or never), colour will (or will not) be used;
  • otherwise, if the NO_COLOR environment variable is set, colour will not be used;
  • otherwise, colour will be used if the output (terminal or file) supports it.


In terminal output, you can enable unicode box-drawing characters to render prettier tables:

  • if the --pretty option is given a value of yes or always (or no or never), unicode characters will (or will not) be used;
  • otherwise, unicode characters will not be used.


When showing long output in the terminal, hledger will try to use the pager specified by the PAGER environment variable, or less, or more. (A pager is a helper program that shows one page at a time rather than scrolling everything off screen). Currently it does this only for help output, not for reports; specifically,

  • when listing commands, with hledger
  • when showing help with hledger [CMD] --help,
  • when viewing manuals with hledger help or hledger --man.

Note the pager is expected to handle ANSI codes, which hledger uses eg for bold emphasis. For the common pager less (and its more compatibility mode), we add R to the LESS and MORE environment variables to make this work. If you use a different pager, you might need to configure it similarly, to avoid seeing junk on screen (let us know). Otherwise, you can set the NO_COLOR environment variable to 1 to disable all ANSI output (see Colour).

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


These environment variables affect hledger:

COLUMNS This is normally set by your terminal; some hledger commands (register) will format their output to this width. If not set, they will try to use the available terminal width.

LEDGER_FILE The main journal file to use when not specified with -f/--file. Default: $HOME/.hledger.journal.

NO_COLOR If this environment variable is set (with any value), hledger will not use ANSI color codes in terminal output, unless overridden by an explicit --color/--colour option.



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 addons 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 Editor configuration 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 ;

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.

; Some other date formats are allowed (but, consistent YYYY-MM-DD is useful).


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 temporarily
  • comment for quickly commenting large regions (remember it's there, or you might get confused)


# a comment line
; another commentline
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 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


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. 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:

    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.


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

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:

unclearedrecorded but not yet reconciled; needs review
pendingtentatively reconciled (if needed, eg during a big reconciliation)
clearedcomplete, 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.


After the status mark, but before the description, you can optionally write a transaction "code", enclosed in parentheses. This is a good place to record a check number, or some other important transaction id or reference number.


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


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 containing single spaces, until end of line or a double space)
  • (optional) two or more spaces (or tabs) followed by an amount.

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 nitty-gritty details of "sum up to zero" in Transaction balancing below.)

As a convenience, you can optionally leave one amount blank; hledger will infer what it should be so as to balance the transaction.

Debits and credits

The traditional accounting concepts of debit and credit of course exist in hledger, but we represent them with numeric sign, as described above. 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 words
credit / minus / right / longer words

The two space delimiter

Be sure to notice the unusual separator between the account name and the following amount. Because hledger allows account names with spaces in them, you must separate the account name and amount (if any) by two or more spaces (or tabs). It's easy to forget at first. If you ever see the amount being treated as part of the account name, you'll know you probably need to add another space between them.

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 borrowed from Frank" or "money spent on electricity".

You can use any account names you like, but we usually start with the traditional accounting categories, which in english are assets, liabilities, equity, revenues, expenses. (You might see these referred to as A, L, E, R, X for short.)

For more precise reporting, we usually divide the top level accounts into more detailed subaccounts, by writing a full colon between account name parts. For example, from the account names assets:bank:checking and expenses:food, hledger will infer this hierarchy of five accounts:


Shown as an outline, the hierarchical tree structure is more clear:


hledger reports can summarise the account tree to any depth, so you can go as deep as you like with subcategories, but keeping your account names relatively simple may be best when starting out.

Account names may be capitalised or not; they may contain letters, numbers, symbols, or single spaces. Note, when an account name and an amount are written on the same line, they must be separated by two or more spaces (or tabs).

Parentheses or brackets enclosing the full account name indicate virtual postings, described below. Parentheses or brackets internal to the account name have no special meaning.

Account names can be altered temporarily or permanently by account aliases.


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"):


..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:

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:


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:


Decimal marks

A decimal mark can be written as a period or a comma:


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:

  EUR 2.000.000,00
INR 9,99,99,999.00
      1 000 000.00   ; <- ordinary space  
      1 000 000.00   ; <- no-break space


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.


After a posting amount, you can note its cost (when buying) or selling price (when selling) in another commodity, by writing either @ UNITPRICE or @@ TOTALPRICE after it. This indicates a conversion transaction, where one commodity is exchanged for another.

(You might also see this called "transaction price" in hledger docs, discussions, or code; that term was directionally neutral and reminded that it is a price specific to a transaction, but we now just call it "cost", with the understanding that the transaction could be a purchase or a sale.)

Costs are usually written explicitly with @ or @@, but can also be inferred automatically for simple multi-commodity transactions. Note, if costs are inferred, the order of postings is significant; the first posting will have a cost attached, in the commodity of the second.

As an example, here are several ways to record purchases of a foreign currency in hledger, using the cost notation either explicitly or implicitly:

  1. Write the price per unit, as @ UNITPRICE after the amount:

      assets:euros     €100 @ $1.35  ; one hundred euros purchased at $1.35 each
      assets:dollars                 ; balancing amount is -$135.00
  2. Write the total price, as @@ TOTALPRICE after the amount:

      assets:euros     €100 @@ $135  ; one hundred euros purchased at $135 for the lot
  3. Specify amounts for all postings, using exactly two commodities, and let hledger infer the price that balances the transaction. Note the effect of posting order: the price is added to first posting, making it €100 @@ $135, as in example 2:

      assets:euros     €100          ; one hundred euros purchased
      assets:dollars  $-135          ; for $135

Amounts can be converted to cost at report time using the -B/--cost flag; this is discussed more in the Cost reporting section.

Note that the cost normally should be a positive amount, though it's not required to be. This can be a little confusing, see discussion at --infer-market-prices: market prices from transactions.

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:

  a   $1 =  $1
  b      = $-1

  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 included files

Multiple files included with the include directive are processed as if concatenated into one file, preserving their order and the posting order within each file. It means that balance assertions in later files will see balance from earlier files.

And if you have multiple postings to an account on the same day, split across multiple files, and you want to assert the account's balance on that day, you'll need to put the assertion in the right file - the last one in the sequence, probably.

Assertions and multiple -f files

Unlike include, when multiple files are specified on the command line with multiple -f/--file options, balance assertions will not see balance from earlier files. This can be useful when you do not want problems in earlier files to disrupt valid assertions in later files.

If you do want assertions to see balance from earlier files, use include, or concatenate the files temporarily.

Assertions and costs

Balance assertions ignore costs, and should normally be written without one:

  (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:

  usd   $-1
  eur   €-1

  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):

  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

  1. isolating each commodity in a subaccount, and asserting those
  2. and also asserting there are no commodities in the parent account itself:
  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 ==*):

  assets:checking  $10
  assets:savings   $10
  assets            $0 ==* $20  ; assets + subaccounts contains $20 and nothing else

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 --auto with that file
  • or assert the balance calculated without --auto, and never use --auto with 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.

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.

    expenses   1  ; a comment for posting 1
    ; a comment for posting 2
    ; a second comment line for posting 2

Transaction balancing

How exactly does hledger decide when a transaction is balanced ? The general goal is that if you look at the journal entry and calculate the amounts' sum perfectly with pencil and paper, hledger should agree with you.

Real world transactions, especially for investments or cryptocurrencies, often involve imprecise costs, complex decimals, and/or infinitely-recurring decimals, which are difficult or inconvenient to handle on a computer. So to be a practical accounting system, hledger allows some imprecision when checking transaction balancedness. The question is, how much imprecision should be allowed ?

hledger currently decides it based on the commodity display styles: if the postings' sum would appear to be zero when displayed with the standard display precisions, the transaction is considered balanced.

Or equivalently: if the journal entry is displayed with amounts rounded to the standard display precisions (with hledger print --round=hard), and a human with pencil and paper would agree that those displayed amounts add up to zero, the transaction is considered balanced.

This has some advantages: it is fairly intuitive, general not hard-coded, yet configurable when needed. On the downside it means that transaction balancedness is related to commodity display precisions, so eg when using -c/--commodity-style to display things with more than usual precision, you might need to fix some of your journal entries (ie, add decimal digits to make them balance more precisely).

Other PTA tools (Ledger, Beancount..) have their own ways of doing it. Possible improvements are discussed at #1964.

Note: if you have multiple journal files, and are relying on commodity directives to make imprecise journal entries balance, the directives' placement might be important - see commodity directive.


Tags are a way to add extra labels or data fields to transactions, postings, or accounts, which you can then search or pivot on.

A tag is a word, optionally hyphenated, immediately followed by a full colon, in the comment of a transaction, a posting, or an account directive. Eg: 2024-01-01 a transaction ; foo: Note this is an exception to the usual rule that things in comments are ignored.

You can write multiple tags on one line, separated by comma. Or you can write each tag on its own comment line (no comma needed in this case).

For example, here are five different tags: one on the assets:checking account, two on the transaction, and two on the expenses:food posting:

account assets:checking         ; accounttag:

2017/1/16 bought groceries      ; transactiontag-1:
    ; transactiontag-2:
    assets:checking        $-1
    expenses:food           $1  ; postingtag:, another-posting-tag:

Postings also inherit tags from their transaction and their account. And transactions also acquire tags from their postings (and postings' accounts). So in the example above, the expenses posting effectively has all five tags (by inheriting from the account and transaction), and the transaction also has all five tags (by acquiring from the expenses posting).

Tag names

Most non-whitespace characters are allowed in tag names. Eg 😀: is a valid tag.

You can list the tag names used in your journal with the tags command:
hledger tags [NAMEREGEX]

In commands which use a query, you can match by tag name. Eg:
hledger print tag:NAMEREGEX

You can declare valid tag names with the tag directive and then check them with the check command.

Special tags

Some tag names have special significance to hledger. There's not much harm in using them yourself, but some could produce an error message, particularly the date: and type: tags. They are explained elsewhere, but here is a quick list for reference:

Tags you can set to influence hledger's behaviour:

 date                   -- overrides a posting's date
 date2                  -- overrides a posting's secondary date
 type                   -- declares an account's type

Tags hledger adds to indicate generated data:

 t                      -- appears on postings generated by timedot letters
 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
 generated-transaction  -- appears on generated periodic txns (with --verbose-tags)
 generated-posting      -- appears on generated auto postings (with --verbose-tags)
 modified               -- appears on txns which have had auto postings added (with --verbose-tags)
Not displayed, but queryable:
 _generated-transaction -- exists on generated periodic txns (always)
 _generated-posting     -- exists on generated auto postings (always)
 _modified              -- exists on txns which have had auto postings added (always)

Tags hledger uses internally:

 _conversion-matched    -- exists on postings which have been matched with a nearby @/@@ cost annotation

Tag values

Tags can have a value, which is any text after the colon up until a comma or end of line, with surrounding whitespace removed. Ending at comma allows us to write multiple tags on one line, but also means that tag values can not contain commas.

Eg in the following posting, the three tags' values are "value 1", "value 2", and "" (empty) respectively:

    expenses:food   $10    ; foo, tag1: value 1 , tag2:value 2, bar tag3: , baz

Multiple tags with the same name are additive rather than overriding: when the same tag name is seen again with a new value, the new name:value pair is added to the tags. It is not possible to override a previous tag's value or remove a tag.

You can list all the values used for a particular tag in the journal with
hledger tags TAGNAME --values

You can match on tag values with a query like tag:NAMEREGEX=VALUEREGEX


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:

Rewrite account namesalias
Comment out sections of the filecomment
Declare file's decimal mark, to help parse amounts accuratelydecimal-mark
Include other data filesinclude
Generate recurring transactions or budget goals~
Generate extra postings on existing transactions=
Define valid entities to provide more error checkingaccount, commodity, payee, tag
Declare accounts' type and display orderaccount
Declare commodity display stylescommodity
Declare market pricesP

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:

directivewhat it doesends at file end?
accountDeclares an account, for checking all entries in all files;
and its display order and type.
Subdirectives: any text, ignored.
aliasRewrites account names, in following entries until end of current file or end aliases.
Command line equivalent: --alias
commentIgnores part of the journal file, until end of current file or end comment.Y
commodityDeclares 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
decimal-markDeclares 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
includeIncludes entries and directives from another file, as if they were written inline.
Command line alternative: multiple -f/--file
payeeDeclares a payee name, for checking all entries in all files.N
PDeclares 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 accountPrepends a common parent account to all account names, in following entries until end of current file or end apply account.Y
DSets 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.
YSets 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 directivesOther 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. They are ignored except they may contain tags, which are not ignored.

The two-space requirement for same-line account comments is because ; is allowed 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 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, 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 journal files, 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.

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

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 accounts come in several types: assets, liabilities, expenses and so on. This enables easy reports like balancesheet and incomestatement, and filtering by account type with the type: query.

As a convenience, hledger will detect these account types automatically if you are using common english-language top-level account names (described below). But it's more robust to declare accounts' types explicitly, by adding type: tags to their account directives. The tag's value should be one of the five main account types:

  • A or Asset (things you own)
  • L or Liability (things you owe)
  • E or Equity (investment/ownership; balanced counterpart of assets & liabilities)
  • R or Revenue (what you received money from, AKA income; technically part of Equity)
  • X or Expense (what you spend money on; technically part of Equity)

or, it can be (these are used less often):

Subaccounts inherit their parent's type, or they can override it. Here is a typical set of account type declarations:

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

Here are some tips for working with account types.

  • The rules for inferring types from account names are as follows. These are just a convenience that sometimes help new users get going; if they don't work for you, just ignore them and declare your account types. See also Regular expressions.

    If account's name contains this (CI) 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
  • If you declare any account types, it's a good idea to declare an account for all of the account types, because a mixture of declared and name-inferred types can disrupt certain reports.

  • Certain uses of account aliases can disrupt account types. See Rewriting accounts > Aliases and account types.

  • As mentioned above, subaccounts will inherit a type from their parent account. More precisely, an account's type is decided by the first of these that exists:

    1. A type: declaration for this account.
    2. A type: declaration in the parent accounts above it, preferring the nearest.
    3. An account type inferred from this account's name.
    4. An account type inferred from a parent account's name, preferring the nearest parent.
    5. Otherwise, it will have no type.
  • For troubleshooting, you can list accounts and their types with:

    $ hledger accounts --types [ACCTPAT] [-DEPTH] [type:TYPECODES]

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.)




$ 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:

  1. alias directives preceding the journal entry, most recently parsed first (ie, reading upward from the journal entry, bottom to top)
  2. --alias options, 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

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

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:

  a:aa     1
$ hledger print --alias '/.*/='

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:

  old    1
$ hledger print --alias old="new  USD" | hledger -f- print
    new             USD 1

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 --alias assets=bassetts type:a

commodity directive

The commodity directive performs several functions:

  1. 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.

  2. It declares how all amounts in this commodity should be displayed, eg how many decimals to show. See Commodity display style above.

  3. (If no decimal-mark directive 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.

  4. 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.

(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 format is significant. Eg:

commodity $1000.00
commodity 1.000,00 EUR
commodity 1 000 000.0000   ; the no-symbol commodity

Commodities do not have tags (tags in the comment will be ignored).

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 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 .


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 FILEPATH

Only journal files can include, and only journal, timeclock or timedot files can be included (not CSV files, currently).

If the file path does not begin with a slash, it is relative to the current file's folder.

A tilde means home directory, eg: include ~/main.journal.

The path may contain glob patterns to match multiple files, eg: include *.journal.

There is limited support for recursive wildcards: **/ (the slash is required) matches 0 or more subdirectories. It's not super convenient since you have to avoid include cycles and including directories, but this can be done, eg: include */**/*.journal.

The path may also be prefixed to force a specific file format, overriding the file extension (as described in Data formats): include timedot:~/notes/2023*.md.

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, the or foreign exchange market.

The format is:


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


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


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:

  1. Two spaces accidentally added or omitted will cause you trouble - read about this below.
  2. For troubleshooting, show the generated transactions with hledger print --forecast tag:generated or hledger register --forecast tag:generated.
  3. Forecasted transactions will begin only after the last non-forecasted transaction's date.
  4. Forecasted transactions will end 6 months from today, by default. See below for the exact start/end rules.
  5. period expressions can be tricky. Their documentation needs improvement, but is worth studying.
  6. 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.
  7. 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

# every 15th of month in 2023's first quarter:
~ monthly from 2023-04-15 to 2023-06-16
    expenses:utilities          $400

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:

  1. the first day of the default year specified by a recent Y directive
  2. or the date specified with --today
  3. 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


  • 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:


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.

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

  expenses:food    $10

  expenses:gifts   $20
$ hledger print --auto
    expenses:food              $10
    (liabilities:charity)      $-1

    expenses:gifts             $20
    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:

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
  assets:cash    = $0

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:

  (a)             = $1 @ €2
$ hledger print --explicit
    (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


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

  a     5  ; <- commodity-less amount, parsed as $5 and displayed as $5.00

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

    food    $10

end apply account

is equivalent to:

    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


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

year 2010  ; change default year to 2010

2009/1/30  ; specifies the year, not affected
  expenses  1

1/31   ; equivalent to 2010/1/31
  expenses  1

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. If the year is omitted, the primary date's year is assumed. When running reports, the primary (left) date is used by default, but with the --date2 flag (or --aux-date or --effective), the secondary (right) date will be used instead.

The meaning of secondary dates is up to you, but it's best to follow a consistent rule. Eg "primary = the bank's clearing date, secondary = date the transaction was initiated, if different".

Downsides: makes your financial data more complicated, less portable, and less trustworthy in an audit. Keeping the meaning of the two dates consistent requires discipline, and you have to remember which reporting mode is appropriate for a given report. Posting dates are simpler and better.

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
tag         NAME
value       EXPR

See also https://hledger.org/ledger.html for a detailed hledger/Ledger syntax comparison.

Other cost/lot notations

A slight digression for Ledger and Beancount users. Ledger has a number of cost/lot-related notations:

    • expresses a conversion rate, as in hledger
    • when buying, also creates a lot than can be selected at selling time
  • (@) UNITCOST and (@@) TOTALCOST (virtual cost)
    • like the above, but also means "this cost was exceptional, don't use it when inferring market prices".

Currently, hledger treats the above like @ and @@; the parentheses are ignored.

  • {=FIXEDUNITCOST} and {{=FIXEDTOTALCOST}} (fixed price)
    • when buying, means "this cost is also the fixed price, don't let it fluctuate in value reports"
  • {UNITCOST} and {{TOTALCOST}} (lot price)
    • can be used identically to @ UNITCOST and @@ TOTALCOST, also creates a lot
    • when selling, combined with @ ..., specifies an investment lot by its cost basis; does not check if that lot is present
  • and related: [YYYY/MM/DD] (lot date)
    • when buying, attaches this acquisition date to the lot
    • when selling, selects a lot by its acquisition date
  • (SOME TEXT) (lot note)
    • when buying, attaches this note to the lot
    • when selling, selects a lot by its note

Currently, hledger accepts any or all of the above in any order after the posting amount, but ignores them. (This can break transaction balancing.)

For Beancount users, the notation and behaviour is different:

    • expresses a cost without creating a lot, as in hledger
    • when buying (augmenting) or selling (reducing) a lot, combined with {...}: documents the cost/selling price (not used for transaction balancing)
    • when buying (augmenting), expresses the cost for transaction balancing, and also creates a lot with this cost basis attached
    • when selling (reducing),
      • 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

Currently, hledger accepts the {UNITCOST}/{{TOTALCOST}} notation but ignores it.

  • variations: {}, {YYYY-MM-DD}, {"LABEL"}, {UNITCOST, "LABEL"}, {UNITCOST, YYYY-MM-DD, "LABEL"} etc.

Currently, hledger rejects these.


hledger can read CSV files (Character Separated Value - usually comma, semicolon, or tab) containing dated records, automatically converting each record into a transaction.

(To learn about writing CSV, see CSV output.)

For best error messages when reading CSV/TSV/SSV files, make sure they have a corresponding .csv, .tsv or .ssv file extension or use a hledger file prefix (see File Extension below).

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-file 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 Importing CSV data tutorial 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.)

sourceoptionally declare which file to read data from
separatordeclare the field separator, instead of relying on file extension
skipskip one or more header lines at start of file
date-formatdeclare how to parse CSV dates/date-times
timezonedeclare the time zone of ambiguous CSV date-times
newest-firstimprove txn order when: there are multiple records, newest first, all with the same date
intra-day-reversedimprove txn order when: same-day txns are in opposite order to the overall file
decimal-markdeclare the decimal mark used in CSV amounts, when ambiguous
fields listname CSV fields for easy reference, and optionally assign their values to hledger fields
Field assignmentassign a CSV value or interpolated text value to a hledger field
if blockconditionally assign values to hledger fields, or skip a record or end (skip rest of file)
if tableconditionally assign values to hledger fields, using compact syntax
balance-typeselect which type of balance assertions/assignments to generate
includeinline another CSV rules file

Working with CSV tips can be found below, including How CSV rules are evaluated.


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. And, you can specify a different data file by adding a "source" rule:

source ./Checking1.csv

If you specify just a file name with no path, hledger will look for it in your system's downloads directory (~/Downloads, currently):

source Checking1.csv

And if you specify a glob pattern, hledger will read the most recent of the matched files (useful with repeated downloads):

source Checking1*.csv

See also "Working with CSV > Reading files specified by rule".


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 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 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:

date-format %m/%d/%y
# The - makes leading zeros optional.
date-format %-d/%-m/%Y
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


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.


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


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


decimal-mark .


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.

fields list


A fields list (the word fields followed by comma-separated field names) is optional, but convenient. It does two things:

  1. It names the CSV field in each column. This can be convenient if you are referencing them in other rules, so you can say %SomeField instead of remembering %13.

  2. 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


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


  • Interpolation strips outer whitespace (so a CSV value like " 1 " becomes 1 when 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:

  1. CSV field names (CSVFIELD in 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 a fields list, eg:

    fields When, What, Some_Id, Net, Total, Foo, Bar
  2. Special hledger field names (HLEDGERFIELD in 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     $ %Total

    or directly in a fields list:

    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.

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.

  1. amount is 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.

  2. amount-in and amount-out work 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 amount and amount-in/amount-out in 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).
  3. 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.

  4. amountN-in and amountN-out work exactly like the above, but should be used when the CSV has two amount fields. This is analogous to amount-in and amount-out, and those tips also apply here.

  5. Remember that a fields list can also do assignments. So in a fields list if you name a CSV field "amount", that counts as assigning to amount. (If you don't want that, call it something else in the fields list, like "amount_".)

  6. The above don't handle every situation; if you need more flexibility, use an if rule 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 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
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 ,,,,


There are two kinds:

  1. A record matcher is a word or single-line text fragment or regular expression (REGEX), which hledger will try to match case-insensitively anywhere within the CSV record.
    Eg: whole foods

  2. A field matcher is preceded with a percent sign and CSV field name (%CSVFIELD REGEX). hledger will try to match these just within the named CSV field.
    Eg: %date 2023

The regular expression is (as usual in hledger) a POSIX extended regular expression, that also supports GNU word boundaries (\b, \B, \<, \>), and nothing else. If you have trouble, see "Regular expressions" in the hledger manual (https://hledger.org/hledger.html#regular-expressions).

What matchers match

With record matchers, it's important to know that the record matched is not the original CSV record, but a modified one: separators will be converted to commas, and enclosing double quotes (but not enclosing whitespace) are removed. So for example, when reading an SSV file, if the original record was:

2023-01-01; "Acme, Inc.";  1,000

the regex would see, and try to match, this modified record text:

2023-01-01,Acme, Inc.,  1,000

Combining matchers

When an if block has multiple matchers, they are combined as follows:

  • By default they are OR'd (any of them can match)
  • When a matcher is preceded by ampersand (&, at the start of the line) it will be AND'ed with the previous matcher (all in the AND'ed group must match)
  • Added in 1.32 When a matcher is preceded by an exclamation mark (!), it is negated (it must not match).

Note currently there is a limitation: you can't use both & and ! on the same line (you can't AND a negated matcher).

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:

; Comment line that explains MATCHERC
<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 matchers; whenever a matcher 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:



; Comment line which explains MATCHERC


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 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



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, B is 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 use the --rules-file option, 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:

  1. Download CSV from Foo's website, using your browser's defaults
  2. Run hledger import foo-checking.csv.rules to 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:

Setting amounts

Continuing from amount field above, here are more tips for amount-setting:

  1. If the amount is in a single CSV field:

    a. If its sign indicates direction of flow:
    Assign it to amountN, 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
  2. 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 to amountN-in and the other to amountN-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-out

    c. 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 as 1 and none. 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
  3. If you want posting 2's amount converted to cost:
    Use the unnumbered amount (or amount-in and amount-out) syntax.

  4. If the CSV has only balance amounts, not transaction amounts:
    Assign to balanceN, to set a balance assignment on the Nth posting, causing the posting's amount to be calculated automatically. balance with no number is equivalent to balance1. 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: +AMT becomes AMT

  • 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: --AMT or -(AMT) becomes AMT

  • 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):


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:


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

Like amounts in a journal file, the amounts generated by CSV rules like amount1 influence commodity display styles, such as the number of decimal places displayed in reports.

The original amounts as written in the CSV file do not affect display style (because we don't yet reliably know their commodity).

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 really need to). First,

  • include - all includes are inlined, from top to bottom, depth first. (At each include point the file is inlined and scanned for further includes, recursively, before proceeding.)

Then "global" rules are evaluated, top to bottom. If a rule is repeated, the last one wins:

  • skip (at top level)
  • date-format
  • newest-first
  • fields - names the CSV fields, optionally sets up initial assignments to hledger fields

Then for each CSV record in turn:

  • test all if blocks. If any of them contain a end rule, skip all remaining CSV records. Otherwise if any of them contain a skip rule, skip that many CSV records. If there are multiple matched skip rules, the first one wins.
  • collect all field assignments at top level and in matched if blocks. When there are multiple assignments for a field, keep only the last one.
  • compute a value for each hledger field - either the one that was assigned to it (and interpolate the %CSVFIELD references), or a default
  • generate a hledger transaction (journal entry) from these values.

This is all part of the CSV reader, one of several readers hledger can use to parse input files. When all files have been read successfully, the transactions are passed as input 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 adding include common.rules to 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:

07/12/2012,LODGMENT       529898,,10.0,131.21
# bankofireland-checking.csv.rules

# skip the header line

# 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.


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


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
    expenses:misc          $20.00

2012-07-30 (17LA58JSKRD4HDGLNJPI1P9B8DKPVHL) To Adapteva, Inc.  ; status:Completed
    expenses:misc          $25.00
    expenses:fees           $1.00


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
In Progress
Temporary Hold
Update to

# 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.
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

noble benefactor
 account2 revenues:foss donations:darcshub
 comment2 business:

Calm Radio
 account2 expenses:online:apps

electronic frontier foundation
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:


The time logging format of timeclock.el, as read by hledger.

hledger can read time logs in timeclock format. As with Ledger, these are (a subset of) timeclock.el's format, containing clock-in and clock-out entries as in the example below. The date is a simple date. The time format is HH:MM[:SS][+-ZZZZ]. Seconds and timezone are optional. The timezone, if present, must be four digits and is ignored (currently the time is always interpreted as a local time). Lines beginning with # or ; or *, and blank lines, are ignored.

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

hledger treats each clock-in/clock-out pair as a transaction posting some number of hours to an account. Or if the session spans more than one day, it is split into several transactions, one for each day. For the above time log, hledger print generates these journal entries:

$ 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

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 emacs and the built-in timeclock.el, or the extended timeclock-x.el and perhaps the extras in ledgerutils.el

  • at the command line, use these bash aliases: cli 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 use the old ti and to scripts in the ledger 2.x repository. These rely on a "timeclock" executable which I think is just the ledger 2 executable renamed.


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:

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, or y, 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 examples


inc:client1   4
fos:hledger   3h
biz:research  60m


# on this day, 6h was spent on client work, 1.5h on haskell FOSS work, etc.
inc:client1   .... .... .... .... .... ....
fos:haskell   .... ..
biz:research  .

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 


# Activity types:
#  c cleanup/catchup/repair
#  e enhancement
#  s support
#  l learning/research

work:adm  ccecces
$ hledger -f a.timedot print
    (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
$ hledger -f a.timedot bal --pivot t
                1.00  c
                0.50  e
                0.25  s


* 2023 Work Diary
** Q1
*** 2023-02-29
**** DONE
0700 yoga
**** BEGUN
 cleaning  ...
 water plants
  outdoor - one full watering can
  indoor - light watering
**** TODO
adm:planning: trip

Using . as account name separator:

fos.hledger.timedot  4h
fos.ledger           ..
$ hledger -f a.timedot --alias '/\./=:' bal -t
                4.50  fos
                4.00    hledger:timedot
                0.50    ledger


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.


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

    (a)      $1000
$ hledger print
    (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'
    (a)          $1000

or by forcing print to always show decimal digits, with --round:

$ hledger print -c '$1,000.00' --round=soft
    (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, rewrite etc.
  • It shows amounts with their original journal precisions, which may not be consistent.
  • 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, or sql is 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).

Time periods

Report start & end date

By default, most hledger reports will show the full span of time 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 time span, such as the current month. You can specify a start and/or end date using -b/--begin, -e/--end, -p/--period or a date: query (described below). All of these accept the smart date syntax (below).

Some notes:

  • End dates are exclusive, as in Ledger, so you should write the date after the last day you want to see in the report.
  • As noted in reporting options: among start/end dates specified with options, the last (i.e. right-most) option takes precedence.
  • The effective report start and end dates are the intersection of the start/end dates from options and that from date: queries. That is, date:2019-01 date:2019 -p'2000 to 2030' yields January 2019, the smallest common time span.
  • In some cases a report interval will adjust start/end dates to fall on interval boundaries (see below).


-b 2016/3/17begin on St. Patrick's day 2016
-e 12/1end at the start of december 1st of the current year (11/30 will be the last date included)
-b thismonthall transactions on or after the 1st of the current month
-p thismonthall transactions in the current month
date:2016/3/17..the above written as queries instead (.. can also be replaced with -)

Smart dates

hledger's user interfaces accept a "smart date" syntax for added convenience. Smart dates optionally can be relative to today's date, be written with english words, and have less-significant parts omitted (missing parts are inferred as 1). Some examples:

2004/10/1, 2004-01-01, 2004.9.1exact date, several separators allowed. Year is 4+ digits, month is 1-12, day is 1-31
2004start of year
2004/10start of month
10/1month and day in current year
21day in current month
october, octstart of month in current year
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
in n days/weeks/months/quarters/yearsn periods from the current period
n days/weeks/months/quarters/years aheadn periods from the current period
n days/weeks/months/quarters/years ago-n periods from the current period
201812018 digit YYYYMMDD with valid year month and day
2018126 digit YYYYMM with valid year and month

Some counterexamples - malformed digit sequences might give surprising results:

2018136 digits with an invalid month is parsed as start of 6-digit year
201813018 digits with an invalid month is parsed as start of 8-digit year
201812328 digits with an invalid day gives an error
2018010129+ digits beginning with a valid YYYYMMDD gives an error

"Today's date" can be overridden with the --today option, in case it's needed for testing or for recreating old reports. (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 adjustment

When there is a report interval (other than daily), report start/end dates which have been inferred, eg from the journal, are automatically adjusted to natural period boundaries. This is convenient for producing simple periodic reports. More precisely:

  • an inferred start date will be adjusted earlier if needed to fall on a natural period boundary

  • an inferred end date will be adjusted later if needed to make the last period the same length as the others.

By contrast, start/end dates which have been specified explicitly, with -b, -e, -p or date:, will not be adjusted (since hledger 1.29). This makes it possible to specify non-standard report periods, but it also means that if you are specifying a start date, you should pick one that's on a period boundary if you want to see simple report period headings.

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"

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)
  • fortnightly
  • bimonthly (every two months)
  • every day|week|month|quarter|year
  • every N days|weeks|months|quarters|years

Weekly on a custom day:

  • every Nth day of week (th, nd, rd, or st are 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]
  • every Nth WEEKDAYNAME [of month]

Yearly on a custom 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)


-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)


-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


With the --depth NUM option (short form: -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: depth:2, --depth=2 or -2 are equivalent.


One of hledger's strengths is being able to quickly report on a precise subset of your data. Most hledger commands accept query arguments, to restrict their scope. Multiple query terms can be provided to build up a more complex query.

  • By default, a query term is interpreted as a case-insensitive substring pattern for matching account names:

    dining groceries

  • Patterns containing spaces or other special characters must be enclosed in single or double quotes:

    'personal care'

  • These patterns are actually regular expressions, so you can add regexp metacharacters for more precision (see "Regular expressions" above for details):

    'accounts (payable|receivable)'

  • To match something other than account name, add one of the query type prefixes described in "Query types" below:


  • Add a not: prefix to negate a term:


  • Terms with different types are AND-ed, terms with the same type are OR-ed (mostly; see "Combining query terms" below). The following query:

    date:2022 desc:amazon desc:amzn

    is interpreted as:

    date is in 2022 AND ( transaction description contains "amazon" OR "amzn" )

Query types

Here are the types of query term available. Remember these can also be prefixed with not: to convert them into a negative match.

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: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.) 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.

Match by transaction code (eg check number).

Match postings or transactions including any amounts whose currency/commodity symbol is fully matched by REGEX. (For a partial match, use .*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 may need one more level of escaping. So eg to match the dollar sign:
hledger print cur:\\$.

Match transaction descriptions.

Match dates (or with the --date2 flag, secondary dates) within the specified period. PERIODEXPR is a period expression with no report interval. Examples:
date:2016, date:thismonth, date:2/1-2/15, date:2021-07-27..nextquarter.

Match secondary dates within the specified period (independent of the --date2 flag).

Match (or display, depending on command) accounts at or above this depth.

Match with a boolean combination of queries (which must be enclosed in quotes). See Combining query terms below.

Match transaction notes (the part of the description right of |, or the whole description if there's no |).

Match transaction payee/payer names (the part of the description left of |, or the whole description if there's no |).

real:, real:0
Match real or virtual postings respectively.

status:, status:!, status:*
Match unmarked, pending, or cleared transactions respectively.

Match by account type (see Declaring accounts > Account types). TYPECODES is one or more of the single-letter account type codes ALERXCV, case insensitive. Note type:A and type:E will also match their respective subtypes C (Cash) and V (Conversion). Certain kinds of account alias can disrupt account types, see Rewriting accounts > Aliases and account types.

Match by tag name, and optionally also by tag value. (To match only by value, use tag:.=REGEX.)

When querying by tag, note that:

  • 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.

A special query term used automatically in hledger-web only: tells hledger-web to show the transaction register for an account.)

Combining query terms

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.

We also support more complex boolean queries with the expr: prefix. This allows one to combine query terms using and, or, not keywords (case insensitive), and to group them by enclosing in parentheses.

Some examples:

  • Exclude account names containing 'food':

    expr:"not food" (not:food is equivalent)

  • Match things which have 'cool' in the description and the 'A' tag:

    expr:"desc:cool and tag:A" (expr:"desc:cool tag:A" is equivalent)

  • Match things which either do not reference the 'expenses:food' account, or do have the 'A' tag:

    expr:"not expenses:food or tag:A"

  • Match things which either do not reference the 'expenses:food' account, or which reference the 'expenses:drink' account and also have the 'A' tag:

    expr:"expenses:food or (expenses:drink and tag:A)"

expr: has a restriction: date: queries may not be used inside or expressions. That would allow disjoint report periods or disjoint result sets, with unclear semantics for our reports.

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.)


Normally, hledger groups and sums amounts within each account. The --pivot FIELD option substitutes some other transaction field for account names, causing amounts to be grouped and summed by that field's value instead. FIELD can be any of the transaction fields acct, status, code, desc, payee, note, or a tag name. When pivoting on a tag and a posting has multiple values of that tag, only the first value is displayed. Values containing colon:separated:parts will be displayed hierarchically, like account names. Multiple, colon-delimited fields can be pivoted simultaneously, generating a hierarchical account name.

Some 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

Pivoted balance report, using member: tag values instead:

$ hledger balance --pivot member
               2 EUR
              -2 EUR  John Doe

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 pivots:

$ hledger balance Income:Dues --pivot kind:member
              -2 EUR  Lifetime:John Doe
              -2 EUR

Generating data

hledger has several features for generating data, such as:

  • Periodic transaction rules can generate single or repeating transactions following a template. These are usually dated in the future, eg to help with forecasting. They are activated by the --forecast option.

  • The balance command's --budget option uses these same periodic rules to generate goals for the budget report.

  • Auto posting rules can generate extra postings on certain matched transactions. They are always applied to forecast transactions; with the --auto flag they are applied to transactions recorded in the journal as well.

  • The --infer-equity flag infers missing conversion equity postings from @/@@ costs. And the inverse --infer-costs flag infers missing @/@@ costs from conversion equity postings.

Generated data of this kind is temporary, existing only at report time. But you can see it in the output of hledger print, and you can save that to your journal, in effect converting it from temporary generated data to permanent recorded data. This could be useful as a data entry aid.

If you are wondering what data is being generated and why, add the --verbose-tags flag. In hledger print output you will see extra tags like generated-transaction, generated-posting, and modified on generated/modified data. Also, even without --verbose-tags, generated data always has equivalen hidden tags (with an underscore prefix), so eg you could match generated transactions with tag:_generated-transaction.


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.


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
    expenses:rent           $1000
$ hledger print --forecast --today=2023/4/21
2023-05-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    expenses:rent                  $1000

2023-06-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    expenses:rent                  $1000

2023-07-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    expenses:rent                  $1000

2023-08-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    expenses:rent                  $1000

2023-09-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    expenses:rent                  $1000

Here there are no ordinary transactions, so the forecasted transactions begin on the first occurence 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
  • 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 --forecast option.
  • 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, -p or date:
  • Try adding the -E flag 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).


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.

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", for convenience; feel free to mentally translate to "conversion rate" or "selling price" if helpful.

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

  assets:dollars    $-135
  assets:euros       €100 @ $1.35   ; $1.35 per euro (unit cost)

Variant 2

  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

  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

    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                                                                                                                                                

Here are some downsides of this kind of entry:

  • The per-unit cost basis is not easy to read.

  • Instead of -B you must remember to type -B --infer-costs.

  • --infer-costs works 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:

  assets:dollars  -$135
  assets:euros     €100 @ $1.35
$ hledger print --infer-equity
    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.

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


  • 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, or equity:trading, or their subaccounts.

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

Instead of reporting amounts in their original commodity, hledger can convert them to cost/sale amount (using the conversion rate recorded in the transaction), and/or to market value (using some market price on a certain date). This is controlled by the --value=TYPE[,COMMODITY] option, which will be described below. We also provide the simpler -V and -X COMMODITY options, and often one of these is all you need:

-V: Value

The -V/--market flag converts amounts to market value in their default valuation commodity, using the market prices in effect on the valuation date(s), if any. More on these in a minute.

-X: Value in specified commodity

The -X/--exchange=COMM option is like -V, except you tell it which currency you want to convert to, and it tries to convert everything to that.

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 P directive date is used (even if it's in the future)
  • 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. (Note, this has a bug in hledger-ui <=1.31: turning on valuation with the V key always resets it to "end".)

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:

  1. 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-prices flag) inferred from costs.

  2. A reverse market price: the inverse of a declared or inferred market price from B to A.

  3. 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.

  4. 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 -x can 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:

  1. The price commodity from the latest P-declared market price for A on or before valuation date.

  2. 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.)

  3. If there are no P directives at all (any commodity or date) and the --infer-market-prices flag 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 -V will convert, and to what.

  • If you have no P directives, and use the --infer-market-prices flag, 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
    assets:euros        €100

; 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

  (a)      1 A @ 5 B

  (a)      1 A @ 6 B

  (a)      1 A @ 7 B

Show the cost of each posting:

$ hledger -f- print --cost
    (a)             5 B

    (a)             6 B

    (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
    (a)             2 B

    (a)             2 B

With no report period specified, that shows the value as of the last day of the journal (2000-03-01):

$ hledger -f- print --value=end
    (a)             3 B

    (a)             3 B

    (a)             3 B

Show the current value (the 2000-04-01 price is still in effect today):

$ hledger -f- print --value=now
    (a)             4 B

    (a)             4 B

    (a)             4 B

Show the value on 2000/01/15:

$ hledger -f- print --value=2000-01-15
    (a)             1 B

    (a)             1 B

    (a)             1 B

Interaction of valuation and queries

When matching postings based on queries in the presence of valuation, the following happens:

  1. The query is separated into two parts:
    1. the currency (cur:) or amount (amt:).
    2. all other parts.
  2. The postings are matched to the currency and amount queries based on pre-valued amounts.
  3. Valuation is applied to the postings.
  4. 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's wide, you may need to scroll sideways.) 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 amountscostvalue at report end or todayvalue at posting datevalue at report or journal endvalue at DATE/today
balance assertions/assignmentsunchangedunchangedunchangedunchangedunchanged

starting balance (-H)costvalue at report or journal endvalued at day each historical posting was madevalue at report or journal endvalue at DATE/today
starting balance (-H) with report intervalcostvalue at day before report or journal startvalued at day each historical posting was madevalue at day before report or journal startvalue at DATE/today
posting amountscostvalue at report or journal endvalue at posting datevalue at report or journal endvalue at DATE/today
summary posting amounts with report intervalsummarised costvalue at period endssum of postings in interval, valued at interval startvalue at period endsvalue at DATE/today
running total/averagesum/average of displayed valuessum/average of displayed valuessum/average of displayed valuessum/average of displayed valuessum/average of displayed values

balance (bs, bse, cf, is)
balance changessums of costsvalue at report end or today of sums of postingsvalue at posting datevalue at report or journal end of sums of postingsvalue at DATE/today of sums of postings
budget amounts (--budget)like balance changeslike balance changeslike balance changeslike balanceslike balance changes
grand totalsum of displayed valuessum of displayed valuessum of displayed valuedsum of displayed valuessum of displayed values

balance (bs, bse, cf, is) with report interval
starting balances (-H)sums of costs of postings before report startvalue at report start of sums of all postings before report startsums of values of postings before report start at respective posting datesvalue at report start of sums of all postings before report startsums of postings before report start
balance changes (bal, is, bs --change, cf --change)sums of costs of postings in periodsame as --value=endsums of values of postings in period at respective posting datesbalance change in each period, valued at period endsvalue 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 endsame as --value=endsums of values of postings from before period start to period end at respective posting datesperiod end balances, valued at period endsvalue at DATE/today of sums of postings
budget amounts (--budget)like balance changes/end balanceslike balance changes/end balanceslike balance changes/end balanceslike balanceslike balance changes/end balances
row totals, row averages (-T, -A)sums, averages of displayed valuessums, averages of displayed valuessums, averages of displayed valuessums, averages of displayed valuessums, averages of displayed values
column totalssums of displayed valuessums of displayed valuessums of displayed valuessums of displayed valuessums of displayed values
grand total, grand averagesum, average of column totalssum, average of column totalssum, average of column totalssum, average of column totalssum, average of column totals

--cumulative is omitted to save space, it works like -H but with a zero starting balance.


Commands overview

Here are the built-in commands:


These data entry commands are the only ones which can modify your journal file.

  • add - add transactions using terminal prompts
  • import - add new transactions from other files, eg CSV files


  • 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



  • balance (bal) - show balance changes, end balances, budgets, gains..
  • print - show transactions or export journal data
  • register (reg) - show postings in one or more accounts & running total
  • roi - show return on investments


  • accounts - show account names
  • activity - show bar charts of posting counts per period
  • 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
  • test - run self tests


  • help - show the hledger manual with info/man/pager
  • demo - show small hledger demos in the terminal


And here are some typical add-on commands. Some of these are installed by the hledger-install script. If installed, they will appear in hledger's commands list:

  • ui - run hledger's terminal UI
  • web - run hledger's web UI
  • iadd - add transactions using a TUI (currently hard to build)
  • interest - generate interest transactions
  • stockquotes - download market prices from AlphaVantage
  • Scripts and add-ons - check-fancyassertions, edit, fifo, git, move, pijul, plot, and more..

Next, each command is described in detail, in alphabetical order.


Show account names.

This command lists account names. By default it shows all known accounts, either used in transactions or declared with account directives.

With query arguments, only matched account names and account names referenced by matched postings are shown.

Or it can show just the used accounts (--used/-u), the declared accounts (--declared/-d), the accounts declared but not used (--unused), the accounts used but not declared (--undeclared), or the first account matched by an account name pattern, if any (--find).

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.

With --types, it also shows each account's type, if it's known. (See Declaring accounts > Account types.)

With --positions, 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 --directives, it adds the account keyword, showing valid account directives which can be pasted into a journal file. This is useful together with --undeclared when updating your account declarations to satisfy hledger check accounts.

The --find flag can be used to look up a single account name, in the same way that the aregister command does. It returns the alphanumerically-first matched account name, or if none can be found, it fails with a non-zero exit code.


$ hledger accounts
$ hledger accounts --undeclared --directives >> $LEDGER_FILE
$ hledger check accounts


Show an ascii barchart of posting counts per interval.

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.


$ hledger activity --quarterly
2008-01-01 **
2008-04-01 *******
2008-10-01 **


Prompt for transactions and add them to the journal. Any arguments will be used as default inputs for the first N prompts.

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.


  • 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.
  • If the journal defines a default commodity, it will be added to any bare numbers entered.
  • 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.

Example (see https://hledger.org/add.html for a detailed tutorial):

$ hledger add
Adding transactions to journal file /src/hledger/examples/sample.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 [2015/05/22]: 
Description: supermarket
Account 1: expenses:food
Amount  1: $10
Account 2: assets:checking
Amount  2 [$-10.0]: 
Account 3 (or . or enter to finish this transaction): .
2015/05/22 supermarket
    expenses:food             $10
    assets:checking        $-10.0

Save this transaction to the journal ? [y]: 
Starting the next transaction (. or ctrl-D/ctrl-C to quit)
Date [2015/05/22]: <CTRL-D> $

If you enter a number with no commodity symbol, and you have declared a default commodity with a D directive, you might expect add to add this symbol for you. It does not do this; we assume that if you are using a D directive you prefer not to see the commodity symbol repeated on amounts in the journal.



Show the transactions and running historical balance of a single account, with each transaction displayed as one line.

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 always included in the running balance (--historical mode is always on).

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: - use aregister for reviewing and reconciling real-world asset/liability accounts - use register for reviewing detailed revenues/expenses.

aregister requires one argument: the account to report on. You can write either the full account name, or a case-insensitive regular expression which will select the alphabetically first matched account.

When there are multiple matches, the alphabetically-first choice can be surprising; eg if you have assets:per:checking 1 and assets:biz:checking 2 accounts, hledger areg checking would select assets:biz:checking 2. It's just a convenience to save typing, so if in doubt, write the full account name, or a distinctive substring that matches uniquely.

Transactions involving subaccounts of this account will also be shown. aregister ignores depth limits, so its final total will always match a balance report with similar arguments.

Any additional arguments form a query which will filter 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.

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.

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.



Show accounts and their balances.

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 higher-level variants of the balance command with convenient defaults, which can be 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 higher-level commands as well.

balance can show..

..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)



  • per period (the default)
  • or accumulated since report start date (--cumulative)
  • or accumulated since account creation (--historical/-H)

..possibly converted to..


  • 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. 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

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

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

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 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 name
    • total - 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

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


  • "Boring" accounts are combined with their subaccount for more compact output, unless --no-elide is used. Boring accounts have no balance of their own and just one subaccount (eg assets:bank and liabilities above).

  • 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

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

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, use one of the higher-level reports, which flip the sign automatically. Eg: hledger incomestatement -MAS).


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 


  • 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/--empty is used.
  • Accounts (rows) containing all zeroes are not shown, unless -E/--empty is used.
  • Amounts with many commodities are shown in abbreviated form, unless --no-elide is used.
  • Average and/or total columns can be added with the -A/--average and -T/--row-total flags.
  • The --transpose flag can be used to exchange rows and columns.
  • The --pivot FIELD option causes a different transaction field to be used as "account name". See PIVOTING.

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:

  1. 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.

  2. Include all of of the account's prior postings in the report, by not specifying a report start date, or by using the -H/--historical flag. (-H causes report start date to be ignored when summing postings.)

Balance report types

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:


Calculation type

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 type

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 type

Which kind of value or cost conversion should be applied, if any, before displaying the report. It is one of:

  • no valuation type : 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 one of the equivalent simpler flags:

  • -B/--cost : like --value=cost (though, note --cost and --value are independent options which can both be used at once)
  • -V/--market : like --value=end
  • -X COMM/--exchange COMM : like --value=end,COMM

See Cost reporting and Value reporting for more about these.

Combining balance report types

Most combinations of these options should produce reasonable reports, but if you find any that seem wrong or misleading, let us know. The following restrictions are applied:

  • --valuechange implies --value=end
  • --valuechange makes --change the default when used with the balancesheet/balancesheetequity commands
  • --cumulative or --historical disables --row-total/-T

For reference, here is what the combinations of accumulation and valuation show:

no valuation--value= then--value= end--value= YYYY-MM-DD /now
--changechange in periodsum of posting-date market values in periodperiod-end value of change in periodDATE-value of change in period
--cumulativechange from report start to period endsum of posting-date market values from report start to period endperiod-end value of change from report start to period endDATE-value of change from report start to period end
--historical /-Hchange from journal start to period end (historical end balance)sum of posting-date market values from journal start to period endperiod-end value of change from journal start to period endDATE-value of change from journal start to period end

Budget report

The --budget report type 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
  income                   $-1950
  expenses:bus                $35
  expenses:food:groceries    $310
  expenses:food:dining        $42
  expenses:movies             $38

  income                   $-2100
  expenses:bus                $53
  expenses:food:groceries    $380
  expenses:food:dining        $32
  expenses:gifts             $100

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:bus and expenses:food are shown because they have budget goals during the report period.

  • Their parent expenses is also shown, with budget goals aggregated from the children.

  • The subaccounts expenses:food:groceries and expenses:food:dining are not shown since they have no budget goal of their own, but they contribute to expenses:food's actual amount.

  • Unbudgeted accounts expenses:movies and expenses:gifts are also not shown, but they contribute to expenses's actual amount.

  • The other unbudgeted accounts income and assets:bank:checking are grouped as <unbudgeted>.

  • --depth or depth: 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/--list mode).

  • Numbers displayed in a --budget report will not always agree with the totals, because of hidden unbudgeted accounts; this is normal. -E/--empty can 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 bare can 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

  expenses:food    $400
$ 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:

is a general option; it enables forecasting with all reportsis a balance command option; it selects the balance report's budget mode
generates visible transactions which appear in reportsgenerates 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 rulesgenerates budget goal transactions throughout the report period, optionally restricted by periods specified in the periodic transaction rules
uses all periodic rulesuses 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 reports show multi-commodity amounts and commodity symbols, which can improve readability. It can also normalise the data for easy consumption by other programs. 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

Here are the --layout modes supported by each output format Only CSV output supports all of them:



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

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

Some useful balance reports

Some frequently used balance options/reports are:

  • bal -M revenues expenses
    Show revenues/expenses in each month. Also available as the incomestatement command.

  • bal -M -H assets liabilities
    Show historical asset/liability balances at each month end. Also available as the balancesheet command.

  • bal -M -H assets liabilities equity
    Show historical asset/liability/equity balances at each month end. Also available as the balancesheetequity command.

  • bal -M assets not:receivable
    Show changes to liquid assets in each month. Also available as the cashflow command.


  • 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



This command displays a balance sheet, showing historical ending balances of asset and liability accounts. (To see equity as well, use the balancesheetequity command.) Amounts are shown with normal positive sign, as in conventional financial statements.

This report shows accounts declared with the Asset, Cash or Liability type (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.


$ 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.



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.

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.


$ 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.



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.

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, checking or saving.

More precisely: all accounts matching this case insensitive regular expression:


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.


Check for various kinds of errors in your data.

hledger provides a number of built-in error checks to help prevent problems in your data. Some of these are run automatically; or, you can use this check command to run them on demand, with no output and a zero exit code if all is well. Specify their names (or a prefix) as argument(s).

Some examples:

hledger check      # basic checks
hledger check -s   # basic + strict checks
hledger check ordereddates payees  # basic + two other checks

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:

Default checks

These checks are run automatically by (almost) all hledger commands:

  • parseable - data files are in a supported format, with no syntax errors and no invalid include directives.

  • autobalanced - all transactions are balanced, after converting to cost. Missing amounts and missing costs are inferred automatically where possible.

  • assertions - all balance assertions in the journal are passing. (This check can be disabled with -I/--ignore-assertions.)

Strict checks

These additional checks are run when the -s/--strict (strict mode) flag is used. Or, they can be run by giving their names as arguments to check:

  • balanced - all transactions are balanced after converting to cost, without inferring missing costs. If conversion costs are required, they must be explicit.

  • accounts - all account names used by transactions have been declared

  • commodities - all commodity symbols used have been declared

Other checks

These checks can be run only by giving their names as arguments to check. They are more specialised and not desirable for everyone:

  • ordereddates - transactions are ordered by date within each file

  • payees - all payees used by transactions have been declared

  • recentassertions - all accounts with balance assertions have a balance assertion within 7 days of their latest posting

  • tags - all tags used by transactions have been declared

  • uniqueleafnames - all account leaf names are unique

Custom checks

A few more checks are are available as separate add-on commands, in https://github.com/simonmichael/hledger/tree/master/bin:

  • hledger-check-tagfiles - all tag values containing / (a forward slash) exist as file paths

  • hledger-check-fancyassertions - more complex balance assertions are passing

You could make similar scripts to perform your own custom checks. See: Cookbook -> Scripting.

More about specific checks

hledger check recentassertions will complain if any balance-asserted account has postings more than 7 days after its latest balance assertion. This aims to prevent the situation where you are regularly updating your journal, but forgetting to check your balances against the real world, then one day must dig back through months of data to find an error. It assumes that adding a balance assertion requires/reminds you to check the real-world balance. (That may not be true if you auto-generate balance assertions from bank data; in that case, I recommend to import transactions uncleared, and when you manually review and clear them, also check the latest assertion against the real-world balance.)



close generates several kinds of "closing" and/or "opening" transactions, useful in certain situations, including migrating balances to a new journal file, retaining earnings into equity, consolidating balances, or viewing lots. Like print, it prints valid journal entries. You can append or copy these to your journal file(s) when you are happy with how they look.

close currently has six modes, selected by a single mode flag:

close --migrate

This is the most common mode. It prints a "closing balances" transaction that zeroes out all asset and liability balances (by default), and an opposite "opening balances" transaction that restores them again. The balancing account will be equity:opening/closing balances (or another specified by --close-acct or --open-acct).

This is useful when migrating balances to a new journal file at the start of a new year. Essentially, you run hledger close --migrate=NEWYEAR -e NEWYEAR and then copy the closing transaction to the end of the old file and the opening transaction to the start of the new file. The opening transaction sets correct starting balances in the new file when it is used alone, and the closing transaction keeps balances correct when you use both old and new files together, by cancelling out the following opening transaction and preventing buildup of duplicated opening balances. Think of the closing/opening pair as "moving the balances into the next file".

You can close a different set of accounts by providing a query. Eg if you want to include equity, you can add assets liabilities equity or type:ALE arguments. (The balancing account is always excluded.) Revenues and expenses usually are not migrated to a new file directly; see --retain below.

The generated transactions will have a start: tag, with its value set to --migrate's NEW argument if any, for easier matching or exclusion. When NEW is not specified, it will be inferred if possible by incrementing a number (eg a year number) within the default journal's main file name. The other modes behave similarly.

close --close

This prints just the closing balances transaction of --migrate. It is the default behaviour if you specify no mode flag. Using the customisation options below, you can move balances from any set of accounts to a different account.

close --open

This prints just the opening balances transaction of --migrate. It is similar to Ledger's equity command.

close --assert

This prints a "closing balances" transaction (with balances: tag), that just declares balance assertions for the current balances without changing them. It could be useful as documention and to guard against changes.

close --assign

This prints an "opening balances" transaction that restores the account balances using balance assignments. Balance assignments work regardless of any previous balance, so a preceding closing balances transaction is not needed.

However, omitting the closing balances transaction would unbalance equity. This is relatively harmless for personal reports, but it disturbs the accounting equation, removing a source of error detection. So --migrate is generally the best way to set to set balances in new files, for now.

close --retain

This is like --close with different defaults: it prints a "retain earnings" transaction (with retain: tag), that transfers revenue and expense balances to equity:retained earnings.

This is a different kind of closing, called "retaining earnings" or "closing the books"; it is traditionally performed by businesses at the end of each accounting period, to consolidate revenues and expenses into the main equity balance. ("Revenues" and "expenses" are actually equity by another name, kept separate temporarily for reporting purposes.)

In personal accounting you generally don't need to do this, unless you want the balancesheetequity report to 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 balancing account, with --close-acct=ACCT and/or --open-acct=ACCT
  • the transaction descriptions, with --close-desc=DESC and --open-desc=DESC
  • the transaction's tag value, with a --MODE=NEW option argument
  • the closing/opening dates, with -e OPENDATE

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 --migrate -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 start: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:start=2021 or not tag:start'
$ hledger bs -Y -f 2021.j -f 2022.j           expr:'tag:start=2021 or not tag:start'
$ hledger bs -Y -f 2022.j -f 2023.j           expr:'tag:start=2022 or not tag:start'
$ hledger bs -Y -f 2021.j                     expr:'tag:start=2021 or not tag:start'
$ hledger bs -Y -f 2022.j                     expr:'tag:start=2022 or not tag:start'
$ hledger bs -Y -f 2023.j                     # unclosed file, no query needed
More detailed close examples

See examples/multi-year.


List the codes seen in transactions, in the order parsed.

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.


2022/1/1 (123) Supermarket   
 Food       $5.00

2022/1/2 (124) Post Office
 Postage    $8.32

2022/1/3 Supermarket
 Food      $11.23

2022/1/4 (126) Post Office
 Postage    $3.21
$ hledger codes
$ hledger codes -E



List all commodity/currency symbols used or declared in the journal.


Play demos of hledger usage in the terminal, if asciinema is installed.

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.

Other asciinema options can be added following a double dash, eg -- -i.1 to limit pauses or -- -h to list asciinema's other options.

During playback, several keys are available: SPACE to pause/unpause, . to step forward (while paused), CTRL-c quit.


$ 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


List the unique descriptions that appear in transactions.

This command lists the unique descriptions that appear in transactions, in alphabetic order. You can add a query to select a subset of transactions.


$ hledger descriptions
Store Name
Gas Station | Petrol
Person A


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.

More precisely, for each posting affecting this account in either file, it looks for a corresponding posting in the other file which posts the same amount to the same account (ignoring date, description, etc.) Since postings not transactions are compared, this also works when multiple bank transactions have been combined into a single journal entry.

This 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.


$ 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:


List all files included in the journal. With a REGEX argument, only file names matching the regular expression (case sensitive) are shown.


Show the hledger user manual in the terminal, with info, man, or a pager. With a TOPIC argument, open it at that topic if possible. TOPIC can be any heading in the manual, or a heading prefix, case insensitive. Eg: commands, print, forecast, journal, amount, "auto postings".

This command shows the hledger manual built in to your hledger version. It can be useful when offline, or when you prefer the terminal to a web browser, or when the appropriate hledger manual or viewing tools are not installed on your system.

By default it chooses the best viewer found in $PATH, trying (in this order): info, man, $PAGER, less, more. 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 the command is run non-interactively, it just prints the manual to stdout.

If using info, note that version 6 or greater is needed for TOPIC lookup. If you are on mac you will likely have info 4.8, and should consider installing a newer version, eg with brew install texinfo (#1770).


$ hledger help --help      # show how the help command works
$ hledger help             # show the hledger manual with info, man or $PAGER
$ hledger help journal     # show the journal topic in the hledger manual
$ hledger help -m journal  # show it with man, even if info is installed


Read new transactions added to each FILE provided as arguments since last run, and add them to the journal. Or with --dry-run, just print the transactions that would be added. Or with --catchup, just mark all of the FILEs' current transactions as imported, without importing them.

This command may append new transactions 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 add).

Unlike other hledger commands, with import the journal file is an output file, and will be modified, though only by appending (existing data will not be changed). The input files are specified as arguments, so to import one or more CSV files to your main journal, you will run hledger import bank.csv or perhaps hledger import *.csv.

Note you can import from any file format, though CSV files are the most common import source, and these docs focus on that case.


import tries to import only the transactions which are new since the last import, ignoring any that it has seen in previous runs. So if your bank's CSV includes the last three months of data, you can download and import it every month (or week, or day) and only the new transactions will be imported each time.

It works as follows. For each imported FILE (usually CSV, but they could be any of hledger's input formats):

  • It tries to recall the latest date seen previously, reading it from a hidden .latest.FILE in the same directory.
  • Then it processes FILE, ignoring any transactions on or before the "latest seen" date.

And after a successful import, it updates the .latest.FILE(s) for next time (unless --dry-run was used).

This is a limited kind of deduplication, let's call it "date skipping". Within each input file, it avoids reprocessing the same dates across successive runs. This is a simple system that works for most real-world CSV files; it assumes these are true, or true enough:

  1. new items always have the newest dates
  2. item dates are stable across successive downloads
  3. the order of same-date items is stable across downloads
  4. the name of the input file is stable across downloads

If you have a bank whose CSV dates or ordering occasionally change, you can reduce the chance of this happening in new transactions by importing more often, and in old transactions it doesn't matter. And remember you can use CSV rules files as input, which is one way to ensure a stable file name.

import doesn't detect other kinds of duplication, such as duplicate transactions within a single run. (In part, because legitimate duplicate transactions can easily occur in real-world data.) So, say you downloaded but forgot to import bank.1.csv, and a week later you downloaded bank.2.csv with overlapping data. Now you should not import both of these at once (hledger import bank.1.csv bank.2.csv); the overlapping transactions which appear twice would not be deduplicated since this is considered a single import. Instead, import these files one at a time, and also use the same filename each time for a common "latest seen" state:

$ mv bank.1.csv bank.csv; hledger import bank.csv
$ mv bank.2.csv bank.csv; hledger import bank.csv

Normally you can ignore the .latest.* files, but if needed, you can delete them (to make all transactions unseen), or construct/modify them (to catch up to a certain date). The format is just a single ISO-format date (YYYY-MM-DD), possibly repeated on multiple lines. It means "I have seen transactions up to this date, and this many of them occurring on that date".

hledger print --new also uses and updates these .latest.* files, but it is less often used.

Related: CSV > Working with CSV > Deduplicating, importing.

Import testing

With --dry-run, the transactions that will be imported are printed to the terminal, without updating your journal or state files. The output is valid journal format, like the print command, so you can re-parse it. Eg, to see any importable transactions which CSV rules have not categorised:

$ hledger import --dry bank.csv | hledger -f- -I print unknown

or (live updating):

$ ls bank.csv* | entr bash -c 'echo ====; hledger import --dry bank.csv | hledger -f- -I print unknown'

Note: when importing from multiple files at once, it's currently possible for some .latest files to be updated successfully, while the actual import fails because of a problem in one of the files, leaving them out of sync (and causing some transactions to be missed). To prevent this, do a --dry-run first and fix any problems before the real import.

Importing balance assignments

Entries added by import will have their posting amounts made explicit (like hledger print -x). This means that any balance assignments in imported files must be evaluated; but, imported files don't get to see the main file's account balances. As a result, importing entries with balance assignments (eg from an institution that provides only balances and not posting amounts) will probably generate incorrect posting amounts. To avoid this problem, use print instead of import:

$ hledger print IMPORTFILE [--new] >> $LEDGER_FILE

(If you think import should leave amounts implicit like print does, please test it and send a pull request.)

Commodity display styles

Imported amounts will be formatted according to the canonical commodity styles (declared or inferred) in the main journal file.



This command displays an income statement, showing revenues and expenses during one or more periods. Amounts are shown with normal positive sign, as in conventional financial statements.

This report 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.


$ 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.


List the unique notes that appear in transactions.

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).


$ hledger notes


List the unique payee/payer names that appear in transactions.

This command lists unique payee/payer names which have been declared with payee directives (--declared), used in transaction descriptions (--used), or both (the default).

The payee/payer 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. This implies --used.


$ hledger payees
Store Name
Gas Station
Person A


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.

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.

Show transaction journal entries, sorted by date.

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.


$ 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

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.

Amounts are shown right-aligned within each transaction (but not aligned across all transactions; you can do that with ledger-mode in Emacs).

Amounts will be (mostly) normalised to their commodity display style: their symbol placement, decimal mark, and digit group marks will be made consistent. By default, decimal digits are shown as they are written in the journal.

With the --round (Added in 1.32) option, print will try increasingly hard to display decimal digits according to the commodity display styles:

  • --round=none show amounts with original precisions (default)
  • --round=soft add/remove decimal zeros in amounts (except costs)
  • --round=hard round amounts (except costs), possibly hiding significant digits
  • --round=all round all amounts and costs

soft is good for non-lossy cleanup, formatting amounts more consistently where it's safe to do so.

hard and all can cause print to show invalid unbalanced journal entries; they may be useful eg for stronger cleanup, with manual fixups when needed.

print's output is usually a valid hledger journal, and you can process it again with a second hledger command. This can be useful for certain kinds of search (though the same can be achieved with expr: queries now):

# 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

There are some situations where print's output can become unparseable:

With -B/--cost, amounts with costs are shown converted to cost.

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.

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 --alias options to bring your accounts into compliance.)
  • An open directive 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
"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.)



Show postings and their running total.

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 --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:

$ 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 1h
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.

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 uses the full terminal width by default, except on windows. You can override this by setting the COLUMNS environment variable (not a bash shell variable) or by using 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
$ COLUMNS=100 hledger reg         # set with one-time environment variable
$ export COLUMNS=100; hledger reg # set till session end (or window resize)
$ hledger reg -w 100,40           # set overall width 100, description width 40
$ hledger reg -w $COLUMNS,40      # use terminal width, & 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.


Print all transactions, rewriting the postings of matched transactions. For now the only rewrite available is adding new postings, like print --auto.

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.


$ 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.


$ 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
+    (liabilities:tax)                0
@@ -22,3 +23,4 @@
 2008/06/01 gift
-    assets:bank:checking  $1
+    assets:bank:checking            $1
+    (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:


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.


Shows the time-weighted (TWR) and money-weighted (IRR) rate of return on your investments.

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.


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.



Show journal and performance statistics.

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.

The default output is fairly impersonal, though it reveals the main file name. With -v/--verbose, more details are shown, like file paths, included files, and commodity names.

It also shows some run time statistics:

  • elapsed time
  • throughput: the number of transactions processed per second
  • live: the peak memory in use by the program to do its work
  • alloc: the peak memory allocation from the OS as seen by GHC. Measuring this externally, eg with GNU time, is more accurate; usually that will be a larger number; sometimes (with swapping?) smaller.

The stats command's run time is similar to that of a balance report.


$ 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

This command supports the -o/--output-file option (but not -O/--output-format).


List the tags used in the journal, or their values.

This command lists the tag names used in the journal, whether on transactions, postings, or account declarations.

With a TAGREGEX argument, only tag names matching this regular expression (case insensitive, infix matched) are shown.

With QUERY arguments, only transactions and accounts matching this query are considered. If the query involves transaction fields (date:, desc:, amt:, ...), the search is restricted to the matched transactions and their accounts.

With the --values flag, 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.)

Tip: remember, accounts also acquire tags from their parents, postings also acquire tags from their account and transaction, transactions also acquire tags from their postings.


Run built-in unit tests.

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!

This command also accepts tasty test runner options, written after a -- (double hyphen). Eg to run only the tests in Hledger.Data.Amount, with ANSI colour codes disabled:

$ hledger test -- -pData.Amount --color=never

For help on these, see https://github.com/feuerbach/tasty#options (-- --help currently doesn't show them).


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)
  • running add-on executables directly simplifies command line parsing (hledger-ui OPTS ARGS)
  • enclose "problematic" args in single quotes
  • if needed, also add a backslash to