hledger

Fast, friendly, robust
plain text accounting software

hledger is...

Here's more about the Features. Don't hesitate to join the Discussion/Support 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.)

Examples

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

Declarations

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
assets
  bank
    checking
    savings
  cash
liabilities
  credit card
equity
  opening/closing
income
  salary
  gifts
expenses
  rent
  food
  gifts

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
    cash

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:


2023/2/1
biz:research  .... ..
fos:hledger   .... .... ....

2023/2/2
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:


"Date","Notes","Amount"
"2023/2/22","DEPOSIT","50.00"
"2023/2/23","TRANSFER TO SAVINGS","-10.00"

# bank.csv.rules  # this rules file tells hledger how to read bank.csv
skip 1
fields date, description, amount
currency $
account1 assets:bank:checking

if WHOLE FOODS
 account2 expenses:food

if (TO|FROM) SAVINGS
 account2 assets:bank:savings
$ hledger -f bank.csv print
2023-02-22 DEPOSIT
    assets:bank:checking          $50.00
    income:unknown               $-50.00

2023-02-23 TRANSFER TO SAVINGS
    assets:bank:checking         $-10.00
    assets:bank:savings           $10.00

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.

News

Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning or any time, tagged with TWIH:.

See also: Release notes

This Week In Hledger 2024-04-19

https://hledger.org/news.html#this-week-in-hledger-2024-04-19

sm Worked on release process, github workflows, platform build issues, mac arm release binaries, CLI commands list, a hledger add ANSI bug, misc doc updates, and released hledger 1.33.

Misc

You can see recent discussions at: https://matrix.hledger.org https://list.hledger.org https://forum.plaintextaccounting.org/latest https://www.reddit.com/r/plaintextaccounting/new https://fosstodon.org/search #hledger

Quotes

Been using ledger and hledger for about four years, love them dearly. I find ledger + Emacs ledger-mode the most convenient for daily use. Hledger has a somewhat nicer way of formatting reports on the CLI, making it useful for creating ad-hoc reports and digging into your data. --compns8-ng

I've been using hledger ... [it] has more features like balance sheet, income statement generation with a plain text file for the last 3 years and it's been working out great. --rwbt


This Week In Hledger 2024-04-12

https://hledger.org/news.html#this-week-in-hledger-2024-04-12

sm Investigated/refactored/discussed a problem with boolean query expressions; disallowed date: in OR expressions #2178. Required process >=1.6.19.0 for HSEC-2024-0003. Updated docs: balance assertions, balancesheetequity.

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

Misc

You can see recent discussions at: https://matrix.hledger.org, https://list.hledger.org, https://forum.plaintextaccounting.org/latest, https://www.reddit.com/r/plaintextaccounting/new, https://fosstodon.org/search -> #hledger. Feel free to share interesting links (they don't have to be new) at the PTA forum.

Quotes

There are few things better than doing my taxes every year and being able to confirm every number on every form with simple #hledger queries (which lets me double check both my records and the forms themselves). --Michael Rees

I combine hledger csv output with Jupyter to generate the specific reports and charts I need. I.e business specific versus combined for taxes. --abhiyerra


This Week In Hledger 2024-04-05

https://hledger.org/news.html#this-week-in-hledger-2024-04-05

sm Doc updates (#2189, #2191, Cookbook#investing, A tax reporting setup). Added Brian Victor's hledger-txnsbycat.hs to the Scripts page.

On plaintextaccounting.org I started a project finance/donations page and public ledger; moved blog links off the front page in favour of the PTA forum; added a new video in Tamil language; updated Investing and trading, FAQ.

Misc

Dmitry Astapov fixed the roi command's handling of --value (#2190).

Ellane W raised thoughtful questions about when to use PTA vs simpler tools like paper.

lil5 announced a Go language client API for hledger-web.

You can see the recent discussion at eg https://matrix.hledger.org, https://list.hledger.org, https://forum.plaintextaccounting.org/latest, https://www.reddit.com/r/plaintextaccounting/new, https://fosstodon.org/search -> #hledger. Feel free to share interesting links (they don't have to be new) at the PTA forum.

Quotes

Having hledger have all my accounts, personal and business and being able to filter out appropriate reports has been great for seeing things like is the business a money pit or am I actually growing wealth over time. --abhiyerra

I use hledger and I love it. It has a learning curve (especially if you're new to double entry accounting) but it's rewarding.


This Week In Hledger 2024-03-29

https://hledger.org/news.html#this-week-in-hledger-2024-03-29

sm Doc rewrites, error message improvements.

Misc

  • More discussion of hledger-interest, a call for improvements, and a pull request by Dmitry Astapov.
  • Some discussion of exporting to Excel/spreadsheets.
  • How much attention to bookkeeping is just right ? blog post

Feel free to share your experience reports or interesting links (they don't have to be new) on the PTA forum.

Quotes

  • The initial setup of the books is pretty time consuming especially if you have a lot of accounts. I recommend starting your most important accounts and adding as you go along. --abhiyerra

This Week In Hledger 2024-03-22

https://hledger.org/news.html#this-week-in-hledger-2024-03-22

sm

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

Misc

Ilya Konovalov made multi-section balance reports always show a section total even when empty, fixing their CSV output. [#2186]

You can see recent discussions at:

https://matrix.hledger.org https://list.hledger.org https://forum.plaintextaccounting.org/latest https://www.reddit.com/r/plaintextaccounting/new https://fosstodon.org/search #hledger

Feel free to share interesting blog posts (they don't have to be new) at the PTA forum.

Quotes

A key advantage of hledger, and why I migrated from ledger, is that hledger internally orders transactions chronologically, whereas ledger inexplicably orders them in the order they appear in the file, irrespective of the date attached to them.--chromatin

hledger can validate date order, that seems to eliminate copy-paste-fail-to-update-date for me. --yencabulator

The reason I have hledger installed is for check ordereddates. --lvass


This Week In Hledger 2024-03-15

https://hledger.org/news.html#this-week-in-hledger-2024-03-15

sm No hledger dev work this week. The new PTA forum continues to work well.

Misc docker-finance, a new cryptocurrency-supporting workflow system using hledger-flow, and the older workflows Full-fledged hledger, hledger-flow and rtrledger, have been added at https://plaintextaccounting.org/#workflows.

To save time, I won't summarise discussions this week; you can see them at:

https://matrix.hledger.org https://list.hledger.org https://forum.plaintextaccounting.org/latest https://www.reddit.com/r/plaintextaccounting/new https://fosstodon.org/search #hledger

Feel free to share interesting blog posts (they don't have to be new) at the PTA forum.

Quotes

  • Way less painful than QuickBooks if you're also a programmer or adjacent. --dmoy

  • My favorite part of the (extensive) Haskell API is Hledger.Cli.Script. It re-exports all the most commonly used functions and data types, meaning you're just one import away from everything you need to get started.--wbadart


This Week In Hledger 2024-03-08

https://hledger.org/news.html#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 ?

Reddit

  • [hledger] Auto posting that excludes previously matched postings
  • Question: Hledger csv first account names in csv files?
  • Discourse for Plain Text Accounting discussions ?

Mastodon

  • online content platforms lock-in
  • PTA forum

Quotes

  • Journal's greenyellow really makes it pop and I think it will make us rich! --sm

  • I started setting up hledger this year and I’m with you for a small business it is absolutely great. --abhiyerra

  • In summary for me: hledger has better csv importing system and ledger has support for lots. I use both. --faustlast


This Week In Hledger 2024-03-01

https://hledger.org/news.html#this-week-in-hledger-2024-03-01

sm A lot of work this week: bugfixing, updating for GHC 9.8, and improving docs. We weren't ready to release 1.33 on March 1st, but it's near.

  • web: show zero amounts with their commodity symbol [#2140]
  • fix expr:'s OR-ing of open-ended dates [#2177]
  • made stats output more private by default, added memory stats
  • in unbalanced transaction error messages, show small imbalances more precisely
  • fixed all ghc 9.8 partial head/tail warnings
  • resolved remaining ghc 9.8-related issues in our dependencies
  • updated all stack configs, switched to nightly/ghc 9.8 for dev builds
  • added support for other unicode space characters in numbers
  • added --round option to close, like print's
  • fixed potential display of ambiguous balance assertion/assignment amounts [#2176]
  • refactored the budget report and fixed wrong omitting of parent accounts [#2071]
  • made budget report's CSV/TSV output show zero instead of empty cells
  • worked on a bugs dashboard
  • doc: fix description of commodity directive's scope [#2135]
  • doc: explain hledger's transaction balancing
  • doc: discuss debits, credits, and sign
  • doc: hledger manual cleanups, fixes, flow improvement
  • doc: FAQ: update why was hledger created
  • doc: hledger and ledger: update performance notes
  • plaintextaccounting.org: link https://lemmy.world/c/plaintextaccounting, a reddit alternative

Robert Nielsen On the hledger fan website, I take a look at needs, strategies, and spending. Also taxes and revisiting lowercasing. https://hledgerfan.com/needs-strategies-and-spending

Misc

Matrix chat wikipedia page, CSV rules, "Personal accounting in Ledger" book, unicode spaces in numbers, rvgp, loan bookkeeping, Open Collective, hledger manual size, file naming, emacs, visual studio code, aggregate shares bookkeeping..

Mail list

  • Forex Accounts Receivable, timing difference between invoice and payment
  • Unexpected behavior using 'expr:'

Reddit

  • 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

Quotes

Quite happy with my switch from GnuCash to hledger: already automated importing the (broken) CSV my bank provides, and getting an overview of average monthly expenses is a one-liner. --rigrig

The other nice part of hledger is the web UI (hledger-web), which is handy for checking out your transactions. --peterhajas


This Week In Hledger 2024-02-23

sm Miscellaneous updates:

  • use a symbolic link's target's directory for relative include paths
  • interpret glob patterns in $LEDGER_FILE
  • implicitly declare the date/date2/type tags
  • add close --assertion-type option
  • tweak close --assert's default description
  • stack:9.8: can now build all packages
  • doc: add Text encoding section
  • doc: clarify description/payee/note
  • doc: improve the tags doc
  • doc: clarify combining csv matchers
  • plaintextaccounting.org: Choosing accounts tips

Robert Nielsen Inspired by a recent podcast, I look at the theme of getting the most good from limited resources. Oh, and I don't forget to mention hledger. 🙂 https://hledgerfan.com/creating-a-positive-impact

Misc Jonathan Dowland added a dark theme to hledger-ui.

Mail list

  • Forex Accounts Receivable, timing difference between invoice and payment
  • Balancing "equity:conversion" split across postings.

Reddit

  • How do you all handle grouping your transactions? (business)
  • hledger: csv rule to match amount higher or equal to 200
  • hledger periodic transaction every month except two
  • Mass edit journal entries based on a filter

Mastodon Fund accounting, PTA sign convention, unspaced account names, reddit monetisation..

Matrix chat Tools for investment tracking, description/payee/note semantics, tag checking, arithmetic amount expressions, Money Manager EX, CSV conversion situations, categorising tools, utf-8 encoding, timedot budgets, tax preparation, if tables, payee cleanup..

Quotes

  • Thank you for writing hledger (and -ui and -web), it's wonderful work. --Michael Iles

  • Use physical order to create mental order.


This Week In Hledger 2024-02-16

sm Not much time for hledger hacking this week; support and doc tweaks, and an updated example CSV rules file for Daedalus (Cardano wallet software).

Robert Nielsen On this week's hledgerfan post, I show how the "include" statement solved a problem for running an hledger command over several data files: https://hledgerfan.com/include-was-the-key

Misc

Matrix chat Accounts vs tags, --serve-api and -X, balance assertion order, hledger.org tls cert, a visualisation of hledger compile times, stateful CSV conversion, chatGPT..

Mail list

  • Balancing "equity:conversion" split across postings.
  • How to set the opening balance in a foreign currency account?

Reddit

  • 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

Mastodon

  • A :hledger: icon is now available on fosstodon.org thanks to @nobodyinperson and @mike.

Quotes

I just discovered hledger about a month ago after being fed up with YNAB's costs and issues. I love hledger. It's amazing. Thank you for all your hard work! --megagram

Work smoothly and steadily. Don't rush.


This Week In Hledger 2024-02-08

sm I worked on refactoring and debugging the budget report for https://github.com/simonmichael/hledger/issues/2071. Also, tweaked close tags, docs, and added to the PTA FAQ: https://plaintextaccounting.org/FAQ#where-are-debits-and-credits-why-are-some-balances-negative. I shared this time report that I like:

# what happened (this week by default ?) Show simplified time categories, largest first, grouping some similar subaccounts together.
@twhat *ARGS:
    hledger -f "$TIMELOG" bal -b thisweek -e tomorrow -WATS -2 --alias '/.*:(res|adm|it)\b/=_:\1' "$@"

Robert Nielsen In this week's post, I share my R program for generating random ledger/hledger transactions. Useful if you want to have some dummy transactions for showing how some feature of hledger works without having to use real data. https://hledgerfan.com/the-code-to-generate-random-hledger-transactions

Matrix chat CSV conversion, bookkeeping situations, mixing balances and changes in a report, include vs -f, tracking saving goals, amazon prime fees, paisa..

Mail list Foreign currency accounting, adding custom tags with close, multiple postings vs multiple transactions..

Reddit

  • Need help with tracking a goal
  • Exporting Paisa interactive visualizations
  • [hledger] How do I get a specific tag column in register report
  • Do you guys enter every transaction manually
  • Thoughts on Beancount and PTA
  • Is it possible to track mileage/mpg with ledger?
  • Can I use ledger for time tracking and conversion from hours to USD?

Mastodon ..crickets..

Misc

Zegnat fixed line breaks in error messages displayed in hledger-web, making them much clearer: https://github.com/simonmichael/hledger/pull/2163

reesmichael1 made ssv: and tsv: work as file path prefixes, like csv:: https://github.com/simonmichael/hledger/pull/2165

jmtd made a dark theme for hledger-ui, testers welcome: https://github.com/simonmichael/hledger/pull/2167

brplot is a fast plotting tool for command line or wasm: https://news.ycombinator.com/item?id=39319191

Quotes

If you know how to get your transactions downloaded from your bank and don't ever want to leave the command line, hledger is great for you -- I've been using hledger since 2019 and love every minute of it.


This Week In Hledger 2024-02-02

sm I cleaned up issue labels, updated the ISSUES page, refined the user pain score, worked on an issues dashboard prototype, tweaked the tags on close's new --assert/--retain modes, and shipped hledger 1.32.3.

Robert Nielsen Working program to generate fake hledger transactions

Matrix chat topics CSV rules, --pivot, queries, -f and glob patterns, QA and bug stats, close, 1.32.3 release, forecasting, rewriting account names, decimal correctness, VAT, mixing changes and balances in a report, tracking subscribers

Mail list 1.32.3 release, foreign currency accounting

Reddit

  • Technical Debt
  • Is this possible with hledger: tracking ACB of RSUs in foreign currency and deemed dispositions when transferring to registered investment accounts?
  • Correct way to track divorce / partner withdrawal in PTA
  • Hledger amount comparison

Quotes

  • SM: too.. many.. bugs..

  • Aankhen: I’m not encountering hledger bugs. It’s just one of the most solid things I’ve ever used.


This Week In Hledger 2024-01-26

sm: Heavy work on

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

Quotes

  • hledger has a decent front-end web UI, it runs as a local server on your machine by default. The filter and searching on this view is quite good, and visualization is decent.

  • With version control and deterministic reports, you can be late, slow, make mistakes, but still keep moving incrementally towards accuracy and clarity.


This Week In Hledger 2024-01-19

sm I updated issue tracker labels and ISSUES and REGRESSIONS pages. On plaintextaccounting.org, I added tables of contents on all pages, tightened bullet lists, ran a link checker and fixed broken links, consolidated to a single more prominent PTA FAQ, and wrote a new "How to organise files" answer.

Robert Nielsen After much procrastination, I finally do the actual changes I had planned for my hledger data. Sed was the tool of choice, and you can read about it at https://hledgerfan.com/first-step-in-closing-out-2023.

Matrix chat docs, csv rules, reports, nix, flycheck-hledger error regions, lot tracking, paycheck entries, annextimelog, tags, paypal, invoicing entries, matrix clients, balance assertions, pivoting, file organisation, receivables, account types

Mastodon 4-year regressions stats

Reddit

  • Tracking an asset like stocks and their growth
  • Protecting financial data with PTA
  • hledger-flow year specific rules file
  • hledger: import from source with different decimal marks
  • Any scripts/documented methods for importing nYNAB (web) data into PTA?

Misc hamess has annotated the new features in version 1.32's manuals.

Quotes

Whoa this is amazing. And really easy!

I love hledger and have a few scripts to convert downloaded CSVs from various institutions into the appropriate format and dedupe any overlap if necessary. Once a month I download a bunch of statements, run a script and have all of the data available to me.


This Week In Hledger 2024-01-12

sm: This week I worked mostly on support and thinking/writing about improvements to close and the new file/periodic files workflow. I introduced some new terminology to distinguish starting/ending files vs opening/closing accounts, which are unrelated events. I added new --assert and --assign modes to close and clarified a problem with using balance assignments for starting balances. I reviewed status and proposed some changes at Better UX around starting and ending files ? #2151 - please take a look and give your thoughts if you're interested in this.

Over on https://plaintextaccounting.org I checked and fixed the remaining broken links; and surveyed and linked all the hledger and PTA videos visible on youtube (they're fun, check them out).

nobodyinperson: TWIH: I published annextimelog v0.10.1 and it's now in a state I would cautiously call useable for something practical as one can now record, delete and also edit logged time periods. It's a cli git-annex based time tracker focusing on conflict-free syncing between devices and flexible tagging. It has timeclock output so can be piped into hledger, e.g.

atl ls -a project=hledger -O timeclock | hledger -f timeclock:- bal -D

https://pypi.org/project/annextimelog

Robert Nielsen: Getting ledger-mode to work again!

Matrix chat topics: CSV rules , Downloading, workflows , MCC codes , PTA videos , PTA site link checking , nix , just , cost basis , emacs & VS code , annextimelog , reporting , SM's time and tasks dashboard

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 ?

Reddit:

  • [hledger] Year Closing
  • How to handle currency exchange via different accounts?
  • Regexp in rules file

Mastodon:

  • Shared expenses bookkeeping
  • just
  • flycheck-hledger
  • importing from your forecast

Open Source Collective, our fiscal host for project funds on Open Collective, may reduce transaction fees: between 15th January and 15th April, you'll see a platform tip request on every new contribution on projects hosted by Open Source Collective. If the trial is a success Open Source Collective will lower our fee on contributions through opencollective.com, making donations more cost-effective for our Collectives.

Quotes

  • each time I get the TWIH ping I get reminded to use hledger again :D But it only shows me how bad I am at managing my money 🙈😬😅

  • hledger is so awesome. I just had to do my entire accounting for the last 1½yrs (because I'm a lazy slob) and thanks to hledger's rule files, it took just one evening.


This Week In Hledger 2024-01-05

sm: I released hledger 1.32.2, which fixes bugs and adds hledger-ui on Windows.

In master I worked on

  • fix check tags to ignore modified, _modified tags automatically #2148
  • fix add,import,web: really avoid filenames ending with . on Windows #1056
  • improve GHC 9.8 support
  • add --assert and --assign modes to close, eg to help try alternate file migration workflows.

In docs:

  • RELEASING: process updates; changelogs are now updated only in release branch
  • REGRESSIONS: split the bounty between finder and fixer
  • sponsors: improve Open Collective tiers, fee info, avatar display
  • add: clarify that 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/

Misc

Quotes

  • Plain-text accounting is great. I choose hledger for the accounts of a small side business that doesn't warrant hiring an accountant.

  • I love hledger and have a few scripts to convert downloaded CSVs from various institutions into the appropriate format and dedupe any overlap if necessary. Once a month I download a bunch of statements, run a script and have all of the data available to me.


This Week In Hledger 2023-12-29

sm: Fixed a tricky csv rule parsing regression #2134 and a debug logging bug.

Improved the project Justfile, improved docs. I also improved and "described" my time and task tracking dashboard; see https://hledger.org/time-and-task-dashboard.html. (Please accept Mr. Goldberg's excellent diagram until I have time to make my own.)

And the usual support and online chatter.

Robert Nielsen: How -U helped me after making an error after mistakenly entering the wrong transactions: https://hledgerfan.com/fixing-my-mistake-after-accidentally-adding-pending-transactions

Misc: Lots of reddit posts this week, including some hledger-related ones:

And also some lively discussion on the mail list, including this proposal to simplify file opening and closing balances.

Quotes: The main thing that made me switch to hledger was the ease of autogenerating transactions into a plain-text format. ... GNUCash made recurring transactions easy but made it a lot harder to generate split transactions according to fixed splits that I regularly engage with my friends/family in. With hledger I have a system of taking CSVs, classifying transactions using a stack of Python scripts and using them to generate hledger importable files. I go by hand and annotate transactions that my scripts couldn't classify/split, and then I import it into my main hledger file. I store all my inputs and intermediate imports in a git repository along with my main ledger file for auditability and to look at annotations. The ease of automation just can't be beat with PTA but it's definitely harder to use if you don't have a preference for TUI work. Ease of writing importers was a huge driving factor when I switched to hledger from GNUCash.


This Week In Hledger 2023-12-22

sm: This week I worked more on consolidating project scripts, porting make rules to just. Also participated in timeclock discussions, did some research and brainstormed next steps.

nobodyinperson: I published annextimelog v0.3.1, a git-annex based time tracker (alpha quality, bare minimum functionality), which can export its data to hledger-readable timeclock.

https://pypi.org/project/annextimelog

Robert Nielsen: On the hledger fan website I look at using desc as a timesaver, and why this matters: https://hledgerfan.com/a-better-description

Misc: ShrykeWindgrace's PR upgrading hledger-ui to the new brick and vty libs has landed; hledger-ui now runs natively on Microsoft Windows.

Quotes of the week: I’ve been using hledger for a few years. ... Every service lets you download historical data as CSV, which can be imported by hledger and turned into journal files and then into visual reports. ... It’s a bespoke setup and super nerdy and is a bit of a pain, but it’s literally the only solution that works for us.


This Week In Hledger 2023-12-15

Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with "TWIH").

sm: Bugfixing

  • Issue triage, ISSUES > Prioritising doc, dashboard, new issue template updates
  • hledger-web: fix --base-url, drop --file-url, better startup messages, improve tests, code cleanups, manual cleanups, code docs.

Robert Nielsen: Discussion of the holiday season and hledger on the hledger fan website: https://hledgerfan.com/the-holiday-season-and-hledger

Misc: budget-cli - budget-focussed, CSV-centric, typescript PTA app with a good write-up

Quotes of the week: I switched, blissfully, away from Quicken/Quickbook/Xero/all that heavy crap to very simple and elegant textfile-based accounting (ledger/hledger) and it's been the best and most flexible accounting experience I've ever had. I finally feel completely in control of my companies' books.


This Week In Hledger 2023-12-08

Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with "TWIH").

sm: Fixed a number of issues with 1.32 and released 1.32.1.

Rewrote the hledger manual's "Budget report" section, and moved some content to the website's Budgeting page.

Tried converting the bake bash scripts to just scripts.

Refactoring, issues, support.

Robert Nielsen: On the hledger fan website, this I discuss one of the most useful ledger mode commands for Emacs. Also, a book recommendation for graphic design that you need to know about: https://hledgerfan.com/upgrading-to-hledger-1-32-a-book-on-graphic-design-and-ledger-mode-clean-buffer

Quotes of the week: I’ve been researching the history of double entry bookkeeping alongside learning the basics of accounting (which is how all this started) and I think hledger and the whole universe of plain text accounting software is super cool. Just a lovely evolution of the method. Thanks for making it so welcoming and accessible


This Week In Hledger 2023-12-01

Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with "TWIH").

sm:

Dev:

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

Ops:

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

Docs:

Use cli class instead of shell for all command-line examples in hledger docs.

hledger: Amount formatting, parseability

Ledger: interoperating tips updates, more about commodity directives and balance assertions.

Videos: reorder hledger fan videos.

REGRESSIONS: cleanups, discuss real names.

Other:

Prepared change notes and released hledger 1.32 with the last quarter's changes.

I did a big cleanup on plaintextaccounting.org, which has absorbed the wiki and now supports Obsidian-style wiki links and clickable headings.

Robert Nielsen: Two things in this week's hledger fan blog. First, for beginners, some notes on reducing errors when reconciling accounts. Second, for the more advanced, a recommendation for an Awk programming book. See the details at https://hledgerfan.com/fewer-errors-in-reconciling/.

Quotes of the week:

It's the basis of my entire personal finance system.

once again, I was deeply impressed by how useful hledger is, and how thoughtful it is.


This Week In Hledger 2023-11-24

Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with "TWIH").

sm:

Dev:

Fixes:

  • Non-print-like reports no longer add trailing decimal marks (a regression in 1.31). Clarified the policy on number formatting adjustments made in certain reports/output formats. (#2115)
  • tag: queries now work when reading CSV files. (#2114)
  • -o/--outputfile with a .json or .sql extension now properly selects those formats.

Improvements/features:

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

Docs:

hledger: added "Amount formatting, parseability", updated "Decimal marks, digit group marks", "commodity directive", "CSV > if table", "Timedot".

Ledger: added more notes on commodity directives, from chat.

Checking for errors: added tips on enforcing account lifetimes, from reddit, and on minimising diffs in reports, from mail list.

Cookbook: added "Reporting version control stats".

Over at https://plaintextaccounting.org/#articles-blog-posts I started adding blog links for 2021-2023, including 10 33 hledger-related posts from Robert Nielsen, Jonathan Dowland, Arsen Arsenović, and Zoran Zaric.

Misc:

Updated project finance scripts, regenerated the project ledger with consistent precisions in assertions, updated it with the last few months of data from Open Collective.

Robert Nielsen: On the theme of the recent Thanksgiving Day, here are three ways you can use hledger to be thankful: https://hledgerfan.com/using-hledger-for-thanksgiving/.

Quotes of the week: Have been taking care of my (new) records for almost two years now and this data is really valuable for me. Gives me very nice overview over everything and confidence that everything is alright and so on. Just looking at some graphs can make you feel really good - in terms of "I know whats happening", not necessarily in "all is well", depending on those actual numbers! 😆 --someone using Beancount


This Week In Hledger 2023-11-17

Every Friday morning (or any time, tagged with "TWIH"): share your hledger/plain text accounting user notes, dev news, achievements or experiences in the hledger matrix chat.

sm:

Dev

A new hledger user pointed out in chat out that -s/--strict mode didn't work with the import command. This exposed a bug (#2113) and led to some reworking of the API in Hledger.Read. Until now, when reading multiple files, -s/--strict checks were performed for each individual file, causing spurious failures, with import and other commands. Now strict checks are done only once, for the overall journal. Also, the import command now only updates .latest files at the end of a successful run (after successfully passing strict checks and after updating the journal file).

Post-"Precisiongeddon" followup: updating hledger-ui and hledger-web to show amounts with correct display precision.

Ops

Fixed a problem with cloudflare authentication that was preventing automatic TLS certificate renewal on hledger.org.

Clarified and documented my cloudflare tokens, and moved them to env vars so the latest caddy config could be committed.

Fixed the hledger.org webhook that was not updating the site on git push.

Enabled https for "code.hledger.org" and "site.hledger.org" short urls.

Fixed some caddy warnings, formatted the config file, added some caddy/webhook management scripts to the site makefile.

Docs

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

Misc

Time breakdown this week: 52% enhancement, 35% cleanup, 13% support.

Robert Nielsen: On the hledger fan website, I discuss using the aregister and print commands to get narrow and wider views of your data: https://hledgerfan.com/zooming-out-and-in

Quotes of the week:

Great software btw, I appreciate your work! Been poking around with PTA and hledger since I was laid off, and it's been incredible for managing/extending my runway 😁 I was a Mint/Personal Capital power user before, but this takes it to the next level. Got a new job and I've already forecasted out the whole next year, very handy and something I could never do before"

I spent some time trying to set up hledger so that I’d actually use it. With multiple accounts it really got tedious but I loved the actual double entry accounting and level of detail. My wife was blown away by the year end summary I made with it. You can see early how much went to taxes, fica etc etc.

I'm really happy how my hledger edit addon helps me quickly reconcile transactions. After CSV-importing from all different sources (phone notes, bank websites, etc.), I pile up the ridiculous amount of invoices and receipts next to me, grab one after the other and execute hledger edit amt:35.11 or hledger edit Checking desc:ALDI for example to find and edit the transaction in my $EDITOR, no matter where that txn was located originally. The other way round (going through the journal, then finding the physical invoice/receipt) turned out to be much slower.

I learned one thing: if %FIELDNAME REGEX matches against FIELDNAME in the CSV. I didn't know that! I always match against the whole line and am annoyed if the order of columns makes this weird


This Week In Hledger 2023-11-10

It's time for This Week In Hledger! Every Friday morning (or any time, tagged with "TWIH"), share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat.

sm:

Dev

This week I completed the months-long yak shave that became Precisiongeddon, and it has landed in master; see https://github.com/simonmichael/hledger/pull/2111 for details. Heads up: this can change default precisions shown especially by cost and value reports - all for the better hopefully, but any pre-release testing is welcome. There's a linux binary at https://github.com/simonmichael/hledger/actions/runs/6804488282.

CI (continuous integration) workflows on github have been optimised a bit:

  • Scheduled weekly builds have been disabled, as they were propagating to forks and running wastefully there in some cases.
  • Some repeated rebuilding of the hledger-lib and hledger packages that seems unnecessary has been stopped.
  • hledger-ui no longer builds its modules twice.
  • Haddock testing has been moved to the release workflows to save time.

Fixed:

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

Docs

As part of Precisiongeddon, I started expanding hledger's "code docs", in the Hledger module's haddock. I spent time learning how to navigate Haddock and producing a fast-feedback authoring workflow (make haddock-watch). And documented a bunch of "jargon" terms, and how we handle precision and display styles.

The "Regular expressions" manual section has been rewritten and now has examples: https://hledger.org/dev/hledger.html#regular-expressions.

Unmentioned last week: I added a News page on hledger.org as a stable home for This Week In Hledger and other news updates.

Misc

Some notable commits:

  • feat: cli: Add tsv output (#869) (Peter Sagerson)
  • feat: import: interpolate regex matches in field templates (#2009) (Jonathan Dowland)

There's new interest and design discussion for referencing the matched account name in auto postings, and I have added to lukasbestle's $20 bounty to make it $100: https://github.com/simonmichael/hledger/issues/1975

Robert Nielsen: For more advanced hledger aficionados, here is a diagram showing just about every possible part of an hledger transaction: https://hledgerfan.com/almost-everything-you-wanted-to-know-about-hledger-transactions.

Quotes of the week:

Overall very happy with hledger so far btw!

And thanks for the amazing tool. Migrating to hledger has been one of the most satisfying bits of digital homesteading I’ve done.


This Week In Hledger 2023-11-03

sm:

Dev

  • tests: add outshine headings for folding/readability (in Emacs), readme updates, include .test files in TAGS, clean up ledger-compat tests
  • tools: bake dbgstrs - print a bunch of hledger debug messages for review
  • tools: ghci :rmain - like :main, but reloads first, useful eg when changing --debug level
  • WIP: imp: prices: fixes, clarify/improve semantics
  • WIP: precisiongeddon: precision/rounding cleanups/fixes/tests/docs, too intricate to describe

Continuing heavy work on consistent and optimal precision handling, and all the related yaks which keep showing up.

Seen

  • https://www.chvp.be/blog/nanoledger/ nice blog post introducing nanoledger android app

Robert Nielsen: Beginner's corner: What are the parts of a basic hledger transaction? Two resources for learners: https://hledgerfan.com/beginners-corner-a-basic-hledger-transaction


This Week In Hledger 2023-10-27

sm:

Dev

  • demo: avoid a bug in asciinema 2.3.0, and improve the error message when asciinema fails (#2094)
  • web: fix broken "File format help" link in the edit form (#2103)
  • web: access control UX cleanups: replace --capabilities and --capabilities-header with --allow, and validate it before starting the app (#834)
  • web: refactor permission checking
  • web: sandstorm web app cleanups; rename/reorder roles & permissions
  • WIP: testing and improving amount style and precision handling in cost/value reports

Ops

  • Set up a self-hosted Sandstorm server and learned how to configure it.
  • Set up a public hledger-web instance in it: sandbox.hledger.org. Unlike https://demo.hledger.org, and until the spammers find it, sandbox is fully writable - use it as a pastebin for examples, eg.

Docs

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

Examples

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

Misc

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

Seen

chvp: A new Android app, NanoLedger, was added to F-droid to facilitate adding transactions on the fly. Payees, notes and accounts can all be autocompleted. Give it a try at https://f-droid.org/en/packages/be.chvp.nanoledger/

Robert Nielsen: Why I created the hledger fan YouTube channel: https://hledgerfan.com/why-i-created-the-hledger-fan-youtube-channel


This Week In Hledger 2023-10-20

G'day @room. It's time for This Week In Hledger ! Every Friday morning, if you have any user or dev news or experiences you'd like to share, post them in the hledger matrix chat (https://matrix.to/#/#hledger:matrix.org) as a message prefixed by "TWIH:". Markdown formatting and edits are welcome. On Saturday I'll publish on the hledger mail list and on mastodon.

sm: Changes merged this week include:

Dev

  • print: add --round option for more control of precisions (#2085)
  • balance-assigned amounts affect commodity styles again (#2091, regression in 1.30)
  • timedot: parse unitful quantities more accurately (#2096)
  • Jacob Weisz has updated the hledger-web app on Sandstorm (The amazing https://sandstorm.io cloud app platform that lets you set up free private hledger-web instances with a few clicks, check it out!)

Ops

  • renamed main CI workflow and branch to "ci"
  • updated and committed hledger.org's caddy config and short urls (redirects)
  • there's now an easy short url for trying out the hledger-web app on Sandstorm: https://sandstorm.hledger.org.

Docs

  • moved regression bounty info to https://hledger.org/REGRESSIONS.html
  • hledger manual > Journal > Commodity display style: rewritten
  • hledger manual > aregister, hledger-ui manual > Register screen: note how separately-dated postings get combined

Examples

  • justfile: forecast-import: show but don't consume future-dated forecast transactions

Robert Nielsen: https://hledgerfan.com/a-not-so-capital-idea-for-saving-time/ describes a timesaving tip for command line users. Specifically, when typing one of your account names, hledger "forgives" you if you don't match the capitalization of an account name.


This Week In Hledger 2023-10-13

[hey there @room - I'd like to get a regular This Week In Hledger started, along the lines of This Week in Matrix (TWIM) ... Having a regular news heartbeat like this, even a small one, will help build our community and momentum.]

Welcome to This Week In Hledger ! Every Friday morning, if you have any user or dev news or experiences you'd like to share, post them in the hledger matrix chat room as a message prefixed by "TWIH:". Markdown formatting welcome, edits welcome. On Saturday I'll publish on the hledger mail list and on mastodon. (Inspired by This Week In Matrix).

sm:

Merged

WIP

  • more control of display precision with print
  • more consistent precision handling in general
  • displaying zeros with consistent commodity symbol, precision etc. when possible
  • bug wrangling

Docs

danielchappell:

What I like about the data format

  • Double Entry accounting in human readable files, the journal files.
  • Balance assignments (to check in on my cash-wallet occasionally)
  • Virtual transactions (to track funds)

What I like about HLedger specifically

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

Welcome! This FAQ is for all hledger-related topics, for now. If you have additions or improvements, please click the "edit this page" link at the bottom, or chat with us.

Accounting

What's accounting ?

Accounting means keeping track of the flow and whereabouts of things you value, such as money or time, and using this information for insight, planning and decision-making. Here's 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.

The hledger project

What's hledger ?

One of the best tools for doing Plain Text Accounting. It's free and you can read all about it at the https://hledger.org home page.

Why was hledger created ?

I needed to make working on finances 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.

hledger and Plain Text Accounting

We use another system, we don't need this ?

Every tool has strengths and weaknesses. hledger is lightweight, flexible and relatively easy to glue into other systems; it might be worth exploring as a complementary tool.

How do you collaborate with accountants and the non-PTA world ?

Depending on their needs, you send them a few standard reports (balance sheet, income statement, itemized account registers or a full transaction journal)

  • as plain text (optionally spruced up with your own templates)
  • or as HTML
  • or as PDF
  • or as CSV they can import into Excel and elsewhere

Must I enter data in a text editor ??

No. A good text editor can be a very efficient way to work on your data, but there are other ways:

  • use a terminal-based data entry tool like hledger add or hledger-iadd
  • use a web-based data entry tool like hledger-web
  • use a phone-based data entry app like MoLe
  • import CSV data, avoiding manual data entry.

What is a good set of account names to start with ?

The chart of accounts (list or tree of account names) will vary according to your language and your accounting situation, and will usually evolve as your needs change. Currently hledger does not have any built-in default chart of accounts. You can start small: name just the accounts you need, as you need them. Or, copy a chart of accounts that seems suitable, from other accounting software or from the web. Some examples can be found at https://plaintextaccounting.org/Choosing-accounts.

What can hledger do for me ?

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.

hledger and other software

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

Using hledger

How do I set environment variables like LEDGER_FILE (persistently) ?

See hledger manual > Setting LEDGER_FILE.

How should I organise files ?

See https://plaintextaccounting.org/Getting-started-FAQ#how-to-organise-files

Why does this entry give a "no amount" error even though I wrote an amount ?

2019-01-01
  a $1
  b

Because there's only a single space between a and 1, so this is parsed as an account named "a 1", with no amount. There must be at least two spaces between account name and amount.

Why does this journal fail strict account checking even though I declared all accounts ?

account assets:bank:checking ; my bank account
account equity               ; equity

2023-01-01
    equity
    assets:bank:checking   $1000

Because there's only a single space between assets:bank:checking and the ; comment, so the comment is parsed as part of the account name. (hledger accounts shows this.) There must be at least two spaces between an account name and anything that follows it.

Why do some directives not affect other files ? Why can't I include account aliases ?

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.

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 ?

Use hledger 1.29+, and just specify the desired start date, eg hledger is -Y -b 2020/4/15 or hledger is -p 'yearly from 2020/4/15'. With older hledger versions, you can approximate it with -p 'every 12 months from 2020/4 or -p 'every 365 days from 2020/4/15'.

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.

Workarounds:

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

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

When importing CSV from two bank accounts, how do I avoid duplicate entries for a transfer between them ?

You can have one of the rules files skip that transaction. Eg if you have checking and savings CSVs, in savings.csv.rules you might have a rule like

# already imported from checking
if TRANSFER FROM .*CHECKING
 skip

Other options: you could remove one of the entries manually. Or you could import both entries, but have them transfer to and from an imaginary third account (but this creates extra noise in your journal and reports).

Customising hledger

How do I install hledger CSV rules for my financial institutions ?

git clone the main hledger repo, and look in examples/csv/ for a rules file you can copy to your financial working directory. If your financial institution is not there yet, please use these for inspiration, ask the #hledger chat for help, and send a pull request contributing your working rules to the repo.

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.

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.

Videos, Talks

hledger-related videos:

Audio only:

See also:

Install

The current hledger release is 1.33. 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 Mac, Windows, GNU/Linux (RSS feed):
hledger release binaries release binaries

Packaged binaries

Homebrew (Mac, Linux, WSL)

Homebrew brew install hledger

Windows

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

Linux

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

Fedora_41 sudo dnf install hledger
Fedora_40
Fedora_39
Fedora_38
Fedora 37
more..

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
ubuntu_23_10
ubuntu_23_04
ubuntu_22_04
ubuntu_20_04
ubuntu_18_04
ubuntu_16_04
more..

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

BSD

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

Docker (Linux, Mac, Windows)

Docker docker pull dastapov/hledger
more..

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

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   # <- 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 with ghcup or from its website. Then:

stack update
stack install --verbosity error --resolver lts-22 \
    hledger-lib-1.33 hledger-1.33 hledger-ui-1.33 hledger-web-1.33

On MS Windows, run this instead:

stack update
stack install --verbosity error --resolver lts-22 \
    hledger-lib-1.33 hledger-1.33 hledger-ui-1.33 hledger-web-1.33 \
    process-1.6.19.0 Cabal-3.10.1.0 haskeline-0.8.2.1 vty-windows-0.2.0.2

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 a a supported version of GHC (8.10 - 9.8). Then:

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

Build in a source checkout

This is the best way to build if you'd like to customise or contribute to hledger. Use git to get the source code for 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

Then:

cd hledger

and you can build and install with:

stack update
stack install

or:

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-...
$ hledger-ui --version
hledger-ui 1.33-...
$ hledger web --version
hledger-web 1.33-...

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

PATH

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.

LANG

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
C
$ export LANG=C.UTF-8    # or en_US.UTF-8, fr_FR.utf8, etc.
$ echo $LANG
C.UTF-8

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

Fixes

  • add, import, web: On MS Windows, don't allow writing to files whose name ends with a period, since it can cause data loss; raise an error instead. I made this change in hledger 1.15 (2019), but it never worked; now it does. #1056

  • balance --budget: The budget report in tree mode was omitting parent accounts with no actual or goal amounts and a single child, instead of showing them as a prefix of the child's name. Now it always shows them, on a line of their own (a bit like --no-elide). It's not a perfect fix, but the budget report code is twisty. #2071

  • check tags: The 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-1.6.19.0+ to avoid any vulnerabilities on Windows from HSEC-2024-0003.

Features

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

Improvements

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

Docs

  • 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

Scripts/addons

  • hledger-bar, hledger-simplebal: shellcheck fixes, cleanups (Colin Dean)

  • hledger-bar: Fix an error when NO_COLOR is not defined #2159. Also, it's now more compliant with the no-color.org spec:

    Command-line software which adds ANSI color to its output by default should check for a NO_COLOR environment variable that, when present and not an empty string (regardless of its value), prevents the addition of ANSI color.

    so one can now temporarily override $NO_COLOR=1 in the environment by setting it empty: NO_COLOR= hledger ...

  • hledger-txnsbycat: added

API

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

hledger-ui 1.33

Fixes

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

Features

  • Add a dark theme. (Jonathan Dowland)

Improvements

  • Allow building with GHC 9.8.

  • Require safe >=0.3.20.

hledger-web 1.33

Fixes

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

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

Improvements

  • Zero amounts are now shown with their commodity symbol. This was mainly to make the sidebar more informative, but also affects and hopefully helps amounts displayed elsewhere. #2140

  • Amounts in the sidebar now also have the amount HTML class.

  • Allow building with GHC 9.8.

  • Require safe >=0.3.20.

Docs

  • 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

Misc

  • Apple ARM binaries are now included in github releases.

Docs

  • REGRESSIONS: we now split the bounty between finder and fixer
  • move Developer docs, MOCKUPS, investment-accounting-features to main repo
  • merge LINKS into dev docs page; cleanup
  • drop unused BACKLOG, TODO pages

credits 1.33

Simon Michael, Jonathan Dowland, Ilja Kocken, Colin Dean, Dmitry Astapov, Vekhir, ShrykeWindgrace, Martijn van der Ven, Michael Rees, aragaer, hamzashezad.

2024-01-28 hledger-1.32.3

hledger 1.32.3

Fixes

  • A performance slowdown since 1.29, especially noticeable with many accounts and transactions, has been fixed. #2153

  • Balance assertions involving mixed-cost balances are checked correctly again (a regression in 1.30). #2150

  • import --catchup works again (a regression in 1.32). #2156

  • --anon is now a deprecated hidden flag that raises an error, but is still usable as --obfuscate (also hidden). #2133

  • Balance assertion error messages are clearer, and show the diff again.

hledger-ui 1.32.3

  • Use hledger-1.32.3

  • Allow vty 6.2, brick 2.3

hledger-web 1.32.3

  • Use hledger-1.32.3

project changes 1.32.3

  • bin/hledger-bar: Fix an error when NO_COLOR is not defined; allow color when NO_COLOR is defined but empty, per no-color spec; and fix shellcheck warnings. #2159 (Colin Dean, Simon Michael)

  • bin/hledger-simplebal: Fix shellcheck warnings. (Colin Dean)

credits 1.32.3

Simon Michael, Colin Dean.

2023-12-31 hledger-1.32.2

hledger 1.32.2

Fixes

  • In CSV field assignments, %FIELD interpolation and \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.

Improvements

  • Allow megaparsec 9.6

Docs

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

hledger-ui 1.32.2

Features

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

Improvements

  • Use Notepad as default editor on Windows (ShrykeWindgrace)

  • Allow brick 2.2 (Vekhir)

  • Allow megaparsec 9.6

hledger-web 1.32.2

Fixes

  • 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

Improvements

  • Allow megaparsec 9.6

  • hledger-web's tests now respect and can test command line options.

  • hledger-web's tests now run the app at 127.0.0.1 and port 5000, rather than "any of our IPv4 or IPv6 addresses" and 3000.

2023-12-07 hledger-1.32.1

hledger 1.32.1

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

Features

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

Improvements

  • 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
    (default)nonenonenonenone
    --round=softsoftnonesoftnone
    --round=hardhardnonehardnone
    --round=allhardhardhardhard
  • 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.

Fixes

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

Docs

  • New:

    • Amount formatting, parseability
    • Started new code docs for developers, based in the Hledger module's haddock
  • Updated:

    • aregister
    • commodity directive
    • Commodity display style
    • if table
    • Decimal marks, digit group marks
    • Regular expressions
    • Timedot

hledger-ui 1.32

Fixes

  • The V key now preserves the valuation mode specified at the command line, if any. (#2084)

  • The hledger-ui package no longer wastefully builds its modules twice.

hledger-web 1.32

Features

  • The hledger-web app on the Sandstorm cloud platform has been updated to a recent version (Jacob Weisz, #2102), and now uses Sandstorm's access control. (Jakub Zárybnický, #821)

Improvements

  • The --capabilities and --capabilities-header options have been replaced with an easier --allow=view|add|edit|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)

Fixes

  • The app can now serve on address 0.0.0.0 (exposing it on all interfaces), which previously didn't work. (#2099) (Philipp Klocke)

  • The broken "File format help" link in the edit form has been fixed. (#2103)

project changes 1.32

Scripts/addons

  • hledger-install.sh: replaced hledger-stockquotes with pricehist

  • added gsheet-csv.hs: fetch a google sheet as CSV

  • added hledger-report1: an example custom compound report, with haskell and bash versions

  • justfile: updated import, time report scripts

Examples

  • New:

    • Fidelity CSV rules
  • Updated:

    • roi-unrealised.ledger (Charlie Ambrose)

Docs

  • New:

    • Started a weekly This Week In Hledger news post, inspired by Matrix.
    • There's now a News page, for This Week In Hledger etc.
    • hledgermatic, an up-to-date, simple journal-first workflow
    • How to record journal entries: added
    • Reporting version control stats: added
    • Moved regression bounty info from the issue tracker to Developer docs > REGRESSIONS.
  • Updated:

    • Checking for errors
    • Common workflows
    • Ledger
    • Simon's old setup
    • Videos
    • All docs now use the cli class instead of shell for command-line examples, avoiding inaccurate highlighting.

Infrastructure

  • hledger.org website:

    • Fixed the webhook that was not updating the site on git push.

    • Fixed a problem with cloudflare authentication that was preventing automatic TLS certificate renewal on hledger.org.

    • Updated and committed hledger.org's caddy config and short urls (redirects)

    • Enabled https for code.hledger.org and site.hledger.org short urls.

    • Updated the stars.hledger.org redirect (we have reached the top 30 github-starred Haskell projects 🌟 🎉).

    • Set up a self-hosted Sandstorm server, and a public hledger-web instance (sandbox.hledger.org) in it that is fully writable (until spammers find it). Use it as a pastebin for examples, eg.

  • Github CI (continuous integration) workflows have been optimised somewhat:

    • Scheduled weekly builds have been disabled, as they were propagating to forks and running wastefully there in some cases.

    • Some repeated rebuilding of the hledger-lib and hledger packages that seems unnecessary has been stopped.

    • hledger-ui no longer builds its modules twice.

    • Haddock testing now done only at release time.

    • renamed main CI workflow and branch to "ci"

  • Tools:

    • .ghci: added an :rmain alias, which is like :main but reloads first - saves typing, and is useful eg when changing --debug level.

    • make haddock-watch is now fast

Finance

  • Updated project finance scripts, regenerated the journal with consistent precisions.

  • Updated reports with the last few months of data from Open Collective.

credits 1.32

Simon Michael, Jonathan Dowland, S. Zeid, Charlie Ambrose, Jacob Weisz, Peter Sagerson, Philipp Klocke, Stephen Morgan, bobobo1618.

2023-09-03 hledger-1.31

More tolerant equity/cost matching; print amounts in original style; multi-pivot.

hledger 1.31

Features

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

Improvements

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

Fixes

  • In journal files, valid multicommodity transactions where the matching non-equity postings can't be auto-detected are no longer considered an error (as they were in hledger 1.29 and 1.30). Now, such transactions are accepted, and --infer-cost has no effect on them. This is similar to the behaviour of --cost, --infer-equity, and --infer-market-prices. (#2045)

  • In journal files, equity conversion postings are now detected more tolerantly, using the same precision as the conversion posting's amount (#2041). Eg, the following transaction is now accepted:

    2023-01-01
        Assets               -84.01 USD @ 2.495 GEL
          ; ^ 209.60495 GEL, recognised as a match for the 209.60 below
        Equity:Conversion     84.01 USD
        Equity:Conversion   -209.60 GEL
        Assets               209.60 GEL
    
  • The roi command now reports TWR per period and overall TWR for multi-period reports. (#2068, Dmitry Astapov)

  • The commands list no longer shows bar when hledger-bar is not installed (#2065), and had a few other cleanups.

hledger-ui 1.31

Improvements

  • Allow megaparsec 9.5

hledger-web 1.31

Improvements

  • Allow aeson 2.2, megaparsec 9.5

project changes 1.31

Scripts/addons

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

  • A justfile implementation of ft and tt

Examples

  • self-tracking

  • RPG ledger (Eric Mertens)

Docs

Infrastructure

  • tools, CI: checkembeddedfiles, checkversions

  • Shake: avoid making empty commits

  • make functest-PAT: runs a subset of functional tests

  • Provide a ghc-tags.yaml file to make use of ghc-tags with Hledger easy.

    ghc-tags is a standalone tool to replace the formerly-built-in ":ctags" feature (and I presume ":etags") in GHCi. These walked over the source and produced a TAGS file (in vim-compatible ctags or Emacs-compatible etags format) that allows the relevant editors to quickly navigate around function definitions.

    ghc-tags trips over some of the CPP used in Hledger. The solution is to provide ghc-tags with explicit CPP defines via a YAML file. However, if a YAML file is provided, one also must specify the source paths, as the tool XORs config file | paths-on-command-line.

    See https://github.com/arybczak/ghc-tags/issues/6 for more information. (Jonathan Dowland)

credits 1.31

Simon Michael, Dmitry Astapov, Eric Mertens, Jay Neubrand, Jonathan Dowland.

2023-06-02 hledger-1.30.1

hledger 1.30.1

Fixes

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

Docs

  • Replace note about repeated options.

2023-06-01 hledger-1.30

Boolean queries, easier CSV file management, built-in demos, hledger-ui cash accounts screen, fixes.

hledger 1.30

Breaking changes

  • The CSV reader now properly skips all empty lines, as specified by docs. Previously, inner empty lines were not being skipped automatically. You might need to adjust the 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)

Features

  • CSV rules files can now be read directly, as in hledger -f foo.csv.rules CMD. By default this will read data from foo.csv in the same directory.

  • CSV rules files can use a new source 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)

Improvements

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

Fixes

  • Unbalanced virtual postings with no amount always infer a zero amount. This is fixing and clarifying the status quo; they always did this, but print always showed them with no amount, even with -x, and the behaviour was undocumented.

  • On windows systems with multiple drive letters, the commands list could fail to show all installed add-ons. (#2040)

  • Balancing a transaction with a balance assignment now properly respects costs. (#2039)

  • The commands list no longer lists non-installed addons. (#2034)

  • Since hledger 1.25, "every Nth day of month" period rules with N > 28 could be calculated wrongly by a couple of days when given certain forecast start dates. Eg ~ every 31st day of 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.

Docs

  • Miscellaneous manual cleanups.

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

  • Add Paging section.

  • Remove archaic mentions of setenv.

API

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

hledger-ui 1.30

Features

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

Improvements

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

Docs

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

  • Miscellaneous manual cleanups.

hledger-web 1.30

Fixes

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

Docs

  • Miscellaneous manual cleanups.

project changes 1.30

Scripts/addons

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

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

Examples

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

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

Docs

  • A shorter, more example-heavy home page on the website.

  • Simplified website and FAQ structure.

credits 1.30

Simon Michael, Chris Lemaire, Yehoshua Pesach Wallach.

2023-04-07 hledger-1.29.2

hledger 1.29.2

Breaking changes

  • 1.29's cleanup of the 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.

    (#2020)

Fixes

  • 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

Improvements

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

Fixes

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

hledger-web 1.29.2

Improvements

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

Fixes

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

project changes 1.29.2

Scripts/addons

  • hledger-install: re-enable hledger-interest, hledger-iadd; add hledger-lots

credits 1.29.2

Simon Michael

2023-03-16 hledger-1.29.1

hledger 1.29.1

Improvements

  • Hledger.Cli.Script now also exports

     Control.Applicative
     Control.Concurrent
     Data.Char
     Data.Functor
     System.IO
     System.IO.Error
    

    and new string helpers

     strip1Char
     stripBy
     strip1By
    
  • Allow building with GHC 9.6.1 (#2011)

Fixes

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

Docs

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

Features

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

Improvements

  • hledger's commands list has been reorganised for clarity. More add-on commands are now recognised and categorised, and unrecognised add-on commands are listed in a more compact multi-column layout. (Simon Michael, Michael Grünewald)

  • hledger's commands list and command line help now use ANSI (bold headings) when supported.

  • hledger's commands list and command line help now use a pager (respecting $PAGER) for long output except on MS Windows.

  • hledger's --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)

Fixes

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

  • Scripts can now use Hledger.Cli.Script, a convenient new prelude which helps reduce import boilerplate. It currently re-exports:

    Control.Monad
    Data.Either
    Data.List
    Data.Maybe
    Data.Ord
    Data.Time
    Text.Printf hiding (formatString)
    Data.Text (Text, pack, unpack)
    Safe hiding (at)
    System.Directory
    System.Environment
    System.Exit
    System.FilePath
    System.Process
    Hledger
    Hledger.Cli
    Hledger.Cli.Main (argsToCliOpts)
    

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

Docs

  • chunk the hledger manual into parts, rename and rearrange sections for better structure/flow
  • add a cheatsheet demonstrating all the main journal features that I recommend
  • move a number of my not-so-recommended journal features into a less visible "Other syntax" section
  • add: payees/descriptions completion
  • areg: more advice on account-matching
  • bal: --budget: clarify use of print --forecast
  • bal: budget: compare with forecasting; add some tips
  • balance cleanups/reorder
  • check: adjacentconversionpostings was dropped
  • cli: balance: fix link to Budgeting page
  • cli: fix all links to Journal > Tags / Commands > tags
  • codes: improve example suggested by Rob Nielsen
  • csv, timeclock, timedot: clarify comment lines (#1953)
  • csv: add new coinbase example
  • csv: clarify amount-in/amount-out docs (#1970)
  • csv: clarify skip/valid csv semantics (#1967)
  • csv: clarify valid CSV requirements and issues (fix #1966)
  • csv: cleanup, reorder, CSV rules tips -> Working with CSV
  • csv: fix wrong if tables doc; rewrite several sections (#1977)
  • csv: flatten, clean up CSV sections
  • csv: improve Amount field / Setting amounts
  • csv: note -in and -out are used together for one posting (#1970)
  • csv: rules factoring tips
  • csv: try to clarify how CSV fields and hledger fields work
  • document --infer-market-prices with signed costs (#1870)
  • fix duplicate market prices heading breaking info navigation
  • import: note a pitfall with multifile import
  • improve Directives summaries
  • introduction/input/output improvements
  • journal: cheatsheet: clarify date tag
  • journal: rewrite Account names, mention brackets/parentheses (#1915)
  • mention pivoting on a tag with multiple values (#1950)
  • more cost notation docs; describe Ledger and Beancount cost notation
  • more mention of posting order effect on inferring cost (#1959)
  • period expressions doc updates
  • Removed redundant paragraph in documentation. (J. B. Rainsberger)
  • rename directive sections, fix many links
  • reorganise commands list, like the CLI
  • reorganise bin/README & the Scripts page, add entries for recent scripts
  • replace "transaction prices" terminology with "costs"
  • tags: discuss multi-values/overriding (#1950)
  • update market price inference docs per sol
  • Updated section on pivoting. Used synonyms for "member" in cases where there could be confusion with the tag named "member." (Robert Nielsen)
  • use more standard and consistent boilerplate in hledger, ui, web man pages
  • virtual postings: improve wording per Robert Nielsen

hledger-ui 1.29

  • In the help dialog, mention that LEFT shows other screens.

  • In the manual, mention shift-up/down config needed for Terminal.app.

hledger-web 1.29

  • The add form's typeahead now shows non-ascii text correctly. (#1961) (Arsen Arsenović)

  • In the manual, improve --base-url's description. (#1562)

project changes 1.29

Scripts/addons

  • hledger-script-example.hs: rename/cleanup
  • sortandmergepostings: new, sorts postings and merges duplicates (Caleb Maclennan, Murukesh Mohanan)
  • hledger-register-max: new, prints the posting with largest historical balance
  • hledger-git: record shows better error output, no longer force-adds ignored files
  • hledger-git: status is fixed, also shows diffs
  • hledger-git: add short command aliases r, s, l
  • hledger-git: -h is fixed
  • hledger-git: pass unrecognised commands to git
  • hledger-install: also install hledger-edit, hledger-plot
  • hledger-install: add support for installing python packages
  • hledger-install: show quieter stack/cabal output
  • hledger-install: align install status list
  • hledger-install: don't list hledger-install.sh in PATH
  • hledger-install: drop hledger-iadd for now https://github.com/hpdeifel/hledger-iadd/issues/71

Docs

  • move most dev docs to doc/
  • Scripting hledger: move plugin types table here
  • Scripts: add hledger-plot, hledger-edit, hledger-fifo (Yann Büchau, Simon Michael)
  • update lots mockups, move to Mockups page
  • split Contributor Guide into Contributor Quick Start, LINKS, ISSUES
  • add REPOS, FILES, DECISIONS
  • CREDITS: updates, link to github contributors list

Infrastructure

  • pr template: mention COMMITS page and prefix convention (#1997)
  • make ghc 9.4 and current stackage nightly the default for dev builds
  • require megaparsec 9.3+ in dev builds, for its useful dbg tool
  • make site-watch: fix runaway recursion, be more verbose
  • new make rules: man-watch
  • new tools: ciwatch, push, pushdocs, gtree
  • misc process updates

credits 1.29

Simon Michael, Chris Lemaire, Caleb Maclennan, Jonathan Dowland, J. B. Rainsberger, Michael Grünewald, Robert Nielsen, Yann Büchau.

2022-12-01 hledger-1.28

new hledger-ui screens, better debug output; accounts, print, csv-reading improvements; new hledger-move, watchaccounts scripts

hledger 1.28

Features

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

    (#1936)

Improvements

  • print --match now respects -o and -O.

  • print --match now returns a non-zero exit code when there is no acceptable match.

  • Support megaparsec 9.3. (Felix Yan)

  • Support GHC 9.4.

Fixes

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

Scripts/addons

  • bin/hledger-move helps record transfers involving subaccounts and costs, eg when withdrawing some or all of an investment balance containing many lots and costs.

  • bin/hledger-git no longer uses the non-existent git record command. (#1942) (Patrick Fiaux)

  • bin/watchaccounts is a small shell script for watching the account tree as you make changes.

hledger-ui 1.28

Features

  • New "Balance sheet accounts" and "Income statement accounts" screens have been added, along with a new top-level "Menu" screen for navigating between these and the "All accounts" screen.

  • hledger-ui now starts in the "Balance sheet accounts" screen by default (unless no asset/liability/equity accounts can be detected, or command line account query arguments are provided). This provides a more useful default view than the giant "All accounts" list. Or, you can force a particular starting screen with the new --menu/--all/--bs/--is flags (eg, hledger-ui --all to replicate the old behaviour).

Improvements

  • The ENTER key is equivalent to RIGHT for navigation.

  • hledger-ui debug output is now always logged to ./hledger-ui.log rather than the console, --debug with no argument is equivalent to --debug=1, and debug output is much more informative.

  • Support GHC 9.4.

  • Support megaparsec 9.3 (Felix Yan)

  • Support (and require) brick 1.5, fsnotify 0.4.x.

Fixes

  • Mouse-clicking in empty space below the last list item no longer navigates back. It was too obtrusive, eg when you just want to focus the window. You can still navigate back with the mouse by clicking the left edge of the window.

  • A possible bug with detecting change of date while in --watch mode has been fixed.

API

  • hledger-ui's internal types have been changed to allow fewer invalid states
    and make it easier to develop and debug. (#1889, #1919).

  • Debug logging helpers have been added and cleaned up in Hledger.Ui.UIUtils: dbgui dbguiIO dbguiEv dbguiScreensEv mapScreens screenId screenRegisterDescriptions

hledger-web 1.28

Improvements

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

  • Allow megaparsec 9.3 (Felix Yan)

  • Support GHC 9.4

project changes 1.28

Docs

  • Miscellaneous improvements.

Examples

  • Indian National Pension Service CSV rules (Pranesh Prakash)

Infrastructure

  • make site-watch: switch from entr to watchexec.

  • make hoogle-setup, hoogle-serve: run a local hoogle on hledger code.

  • make man-watch-PROG: watch a hledger program's man page as source files change.

credits 1.28

Simon Michael, Felix Yan, Patrick Fiaux.

2022-09-18 hledger-1.27.1

hledger 1.27.1

Fixes

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

Fixes

  • The add form no longer gives an error when there is just a single file and no file field showing. (#1932)

  • Uses hledger-1.27.1

2022-09-01 hledger-1.27

Infer costs from equity postings, new error checks, improved error messages, fixes.

hledger 1.27

Features

  • hledger check recentassertions (and flycheck-hledger in Emacs if you enable this check) requires that all balance-asserted accounts have a balance assertion within 7 days before their latest posting.

    This helps remind you to not only record transactions, but also to regularly check account balances against the real world, to catch errors sooner and avoid a time-consuming hunt.

  • The --infer-costs general flag has been added, as the inverse operation to --infer-equity. --infer-costs detects commodity conversion transactions which have been written with equity conversion postings (the traditional accounting notation) and adds PTA cost notation (@@) to them (allowing cost reporting). See https://hledger.org/hledger.html#equity-conversion-postings . (Stephen Morgan)

Improvements

  • Many error messages have been improved. Most error messages now use a consistent, more informative format. (#1436)

  • The accounts command has a new --directives flag which makes it show valid account directives which you can paste into a journal.

  • The accounts command has a new --positions flag which shows where accounts were declared, useful for troubleshooting. (#1909)

  • Bump lower bounds for Diff and githash. (Andrew Lelechenko)

  • GHC 8.6 and 8.8 are no longer supported. Building hledger now requires GHC 8.10 or greater.

Fixes

  • Account display order is now calculated correctly even when accounts are declared in multiple files. (#1909)

  • At --debug 5 and up, account declarations info is logged. (#1909)

  • hledger aregister and hledger-ui now show transactions correctly when there is a type: query. (#1905)

  • bal: Allow cumulative gain and valuechange reports. Previously, --cumulative with --gain or --valuechange would produce an empty report. This fixes this issue to produce a reasonable report. (Stephen Morgan)

  • bal: budget goal amounts now respect -c styles (fixes #1907)

  • bal: budget goals now respect -H (#1879)

  • bal: budget goals were ignoring rule-specified start date

  • cf/bs/is: Fixed non-display of child accounts when there is an intervening account of another type. (#1921) (Stephen Morgan)

  • roi: make sure empty cashflows are skipped when determining first cashflow (Charlotte Van Petegem) Empty cashflows are added when the begin date of the report is before the first transaction.

Scripts/addons

  • https://hledger.org/scripts.html - an overview of scripts and addons in bin/.

  • paypaljson, paypaljson2csv - download txns from paypal API

  • hledger-check-postable.hs - check that no postings are made to accounts with a postable:(n|no) tag

  • hledger-addon-example.hs - script template

hledger-ui 1.27

Improvements

  • At --debug=2 and up, log debug output to ./debug.log.

  • Use/require brick 1.0+. (#1889)

  • Use hledger 1.27

hledger-web 1.27

Improvements

  • Improve the add form's layout and space usage.

  • Pre-fill the add form's date field.

  • Highlight today in the add form's date picker.

  • Focus the add form's description field by default.

  • Allow an empty description in the add form.

  • Use hledger 1.27

Fixes

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

project changes 1.27

Docs

  • https://hledger.org/ERRORS.html - an overview of hledger's error messages.

  • Rewrite/consolidate cost and conversion docs.

  • New template for github releases, with improved install instructions for binaries.

  • Add modern windows binary install instructions. (Lazar Lazarov, Simon Michael)

  • Fix tables of contents in developer documentation. (Alex Hirzel)

  • Update ACHIEVEMENTS. (Alex Hirzel)

  • Corrected the extension for the CREDITS file. (Pranesh Prakash)

  • Fix broken link in bin/README.md. (David D Lowe)

Examples

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

Process

  • Revive github projects, set up http://projects.hledger.org shortcut url

  • Many cleanups and improvements to the CI test and binary-generating github actions. The CI tests for master now also include hledger-lib's doctests.

  • All packages now disallow name shadowing in their code.

  • make scc gives a modern report of code line counts.

  • make ghci-unit-test loads hledger-lib unit tests in GHCI.

credits 1.27

Simon Michael, Stephen Morgan, Alex Hirzel, Pranesh Prakash, David D Lowe, Charlotte Van Petegem, Max Thomas, Andrew Lelechenko.

2022-07-11 hledger-1.26.1

hledger 1.26.1

  • require safe 0.3.19+ to avoid deprecation warning

hledger-ui 1.26.1

  • support doclayout 0.4, brick 0.72+

  • require safe 0.3.19+ to avoid deprecation warning

2022-06-04 hledger-1.26

Miscellaneous improvements.

hledger 1.26

Improvements

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

Fixes

  • 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

Fixes

  • Don't add link URLs when printing.

Improvements

  • Now builds with GHC 9.2.

  • Uses hledger 1.26.

project changes 1.26

Scripts/addons

  • renamed hledger-number.sh to hledger-simplebal

  • added hledger-git, hledger-pijul

  • fin (and bin) scripts show available scripts and their help

  • renamed aliases.sh to bashrc

  • Get hledger-print-location working. (Stephen Morgan)

Docs

  • README cleanup, inspired by feedback from README reviewer Lars Wirzenius.

  • Clearer sponsoring info and more complete sponsor lists on website and README.

  • The new https://github.com/simonmichael/hledger_finance repo keeps track of our public finances (on Open Collective, Liberapay etc.)

Examples

  • invoice: calculate dates accurately on last days of month

Process

  • Stackage nightly and GHC 9.2 are now the default for dev builds.

  • CI workflows:

    • Workflows and binaries have more consistent naming, mentioning platform and architecture.
    • The main test workflow is now linux-x64-test, 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.

Features

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

    2000-01-01
      a             1 AAA @@ 2 BBB
      b            -2 BBB
    
    $ hledger print --infer-equity
    2000-01-01
      a                               1 AAA
      equity:conversion:AAA-BBB:AAA  -1 AAA
      equity:conversion:AAA-BBB:BBB   2 BBB
      b                              -2 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)

Improvements

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

Fixes

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

Documentation

hledger-ui 1.25

  • Uses hledger 1.25.

hledger-web 1.25

  • Uses hledger 1.25.

project changes 1.25

Scripts/addons

  • hledger-install.sh now also installs Pavan Rikhi's hledger-stockquotes tool.

  • The bin/hledger-number addon was added.

  • The bin/hledger-check-fancyassertions addon now shows docs in --help.

  • A new invoice-making script was added: examples/invoicing/invoice-script/invoice

Process/tools

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

Fixes

  • 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

Fixes

  • An extra "root" account is no longer shown (a regression in 1.24). (#1782)

  • Declared accounts are now filtered correctly by a not:ACCT query. (#1783)

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

hledger-web 1.24.1

Fixes

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

2021-12-01 hledger-1.24

New report layout options with less eliding, hledger-ui mouse support, misc fixes and improvements.

hledger 1.24

Features

  • balance commands provide more control over how multicommodity amounts are displayed. (And they no longer elide too-wide amounts by default.) The --commodity-column flag has been deprecated and replaced by a new --layout option, with three values:

    • wide (the default, shows amounts on one line unelided, like older hledger versions)
    • tall (a new display mode, shows one amount per line)
    • bare (like the old --commodity-columm, shows one commodity per line with symbols in their own column)

    (Stephen Morgan)

  • The balance commands have a new --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)

Improvements

  • The stats command now shows rough but useful performance stats: run time and processing speed in transactions per second.

  • balance: support the --related flag, like register, showing the other postings from the transactions. (#1469, Stephen Morgan)

  • roi now uses posting dates when available, and honors the --date2 flag. This will not change the results computed for the typical use-case, it just makes "roi" more thorough/consistent. (Dmitry Astapov)

  • aregister now shows transactions' secondary date if the --date2 flag is used. (#1731)

  • timedot: a D default commodity (and style) declared in a parent journal file will now be applied to timedot amounts. This means they can be priced and valued/converted.

  • cli: The --pretty and --forecast options can now be written after the command name, like other general options. (Stephen Morgan)

  • register -V -H with no interval now values at report end date, like balance. (#1718, Stephen Morgan)

  • Allow megaparsec 9.2.

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

Fixes

  • prices: Do not include zero amounts when calculating amounts for balance assignments. This is not usually a problem, but can get in the way of auto-inferring prices. (#1736, Stephen Morgan)

  • csv: Successfully parse an empty csv file. (#1183, Stephen Morgan)

  • balance: Balance reports with --depth=0 properly report aggregated values, not zero everywhere. (#1761, Stephen Morgan)

  • prices: Do not try to generate prices when there would be a zero denominator. Also correctly generate reverse prices for zero amounts. (Stephen Morgan)

  • csv: Allow both amount-in and amount-out fields to contain a zero. (#1733, Stephen Morgan)

  • balance: Balance reports should consider date: queries when calculating report span with --date2. (#1745, Stephen Morgan)

  • print: auto: The print command should always display inferred amounts for --auto generated postings. (#1276, Stephen Morgan)

hledger-ui 1.24

Features

  • hledger-ui can now be controlled with mouse or touchpad. Click to enter things, click left margin or bottom blank area to return to previous screen, and use mouse wheel / swipe to scroll.

  • In addition to accounts with postings, hledger-ui now also shows declared accounts, even if they are empty (just leaf accounts, not parents). The idea is to show a useful list of accounts out of the box, when all you have is a starter file with account declarations.

Improvements

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

Fixes

  • When an invalid regular expression is entered at the / (filter) prompt, we now display an error instead of silently ignoring it. (#1394, Stephen Morgan)

  • Entering the register screen now always positions the selection mid-screen. Previously it would be at bottom of screen on the first entry.

  • Report layout in the terminal is now robust with more kinds of wide characters, such as emoji. (#895, Stephen Morgan)

hledger-web 1.24

Improvements

  • Allow megaparsec 9.2

project changes 1.24

Software

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

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

Docs

  • Account types: prioritise the short one-letter names, hide the deprecated legacy syntax.

  • Directives: a more compact and accurate overview.

  • examples/templates/basic: A new starter file set, and a place to collect them.

  • Expose more developer docs as separate web pages: CHANGELOGS, COMMITS, RELEASING, etc.

  • Fix a link to developer workflows. (Joaquin "Florius" Azcarate)

Process

  • PR template: Fix our github PR template to use proper comment syntax, and link to more relevant docs. (toonn)

  • cabal.project: Drop obsolete compatibility comment. (#1365, toonn)

  • Bump default stackage snapshot to one avoiding buggy happy version.

  • bin/changelog: a new helper making changelog edits more pleasant.

  • make throughput{,-dev,-EXE}: reports transactions per second for a range of file sizes with the hledger in PATH, hledger dev build, or named hledger executable.

  • make install-as-FOO: build executables and save as bin/hledger*-FOO

  • perf: bench-ledger.sh for comparative benchmarking with Ledger.

  • CI: commitlint: be more forgiving when we can't figure out recent commits (don't check any).

  • CI: commitlint: recognise any commit starting with ‘Merge’ as a merge commit (and ignore it). (Stephen Morgan)

credits 1.24

Simon Michael, Stephen Morgan, toonn, Pranesh Prakash, Dmitry Astapov, ShrykeWindgrace, Joaquin Azcarate, Lawrence Wu.

2021-09-21 hledger-1.23

Capital gains report, separate symbol/number display, command line commodity styling, budget selection, weekday/weekend recurrence, 10% speedup, fixes.

project changes 1.23

Software:

  • The bin/hledger-check-fancyassertions.hs addon script, allowing more complex balance assertions, works again. (#1464, Stephen Morgan)

  • Many code cleanups suggested by hlint (Stephen Morgan)

Docs:

  • Added a public BACKLOG.org to the hledger repo and website.

  • Website updates:

    • Reorganised site content.
    • Improved page tables of contents.
    • Content fixes.
    • New docs: Currency conversion. hledger and Beancount/GnuCash/Ledger/Quicken.
  • New examples: systemd and nginx configs for hledger-web (Alan Young)

Tools/process:

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

Features

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

Improvements

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

Fixes

  • A rare bug causing incorrect balances to be reported by the cf/bs/bse/is commands, since hledger 1.19, has been fixed. (cf/bs/bse/is with --tree --no-elide --begin DATE and certain account directives could show wrong balances). (#1698, Stephen Morgan)

  • aregister now aligns multicommodity amounts properly (broken since 1.21). (#1656, Stephen Morgan)

  • balance -E (and hledger-ui Z) now correctly show zero parent accounts, fixing a bug introduced in hledger 1.19. (#1688, Stephen Morgan)

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

Improvements

  • Depend on hledger 1.23.

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

hledger-web 1.23

Improvements

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

  • Depend on hledger 1.23.

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

Fixes

  • Toggle showing zero items properly even when called with --empty. (#1237, Stephen Morgan)

  • Do not hide empty accounts if they have non-empty subaccounts. (#1237, Stephen Morgan)

  • Allow unbalanced postings (parenthesised account name) in the add transaction form. (#1058, Stephen Morgan)

  • An XSS (cross-site scripting) vulnerability has been fixed. Previously (since hledger-web 0.24), javascript code could be added to any autocompleteable field and could be executed automatically by subsequent visitors viewing the journal. Thanks to Gaspard Baye and Hamidullah Muslih for reporting this vulnerability. (#1525, Arsen Arsenović)

credits 1.23

Simon Michael, Stephen Morgan, Lawrence Wu, Jakob Schöttl, Dmitry Astapov, Malte Brandy, Arsen Arsenović, Arjen Langebaerd, Alan Young, Daniel Gröber.

2021-08-07 hledger-1.22.2

hledger 1.22.2

Breaking changes

  • aregister no longer hides future transactions by default. This is a consequence of the fix for #1638. It makes aregister consistent, so we think it's a reasonable change. So if you have future-dated transactions in your journal which you don't want reported, you now must exclude them with -e tomorrow or date:-tomorrow in the command, as with other reports. (Stephen Morgan)

Improvements

  • Timedot format's doc has been rewritten.

Fixes

  • Make balance assignments in forecasted transactions work again (broken in 1.22.1). Forecast transactions are now generated early and processed in the same way as other transactions. (#1638, Stephen Morgan)

  • aregister preserves the order of same-day transactions again (broken in 1.22.1). (#1642, Stephen Morgan)

hledger-ui 1.22.2

  • Use hledger 1.22.2.

hledger-web 1.22.2

  • Use hledger 1.22.2.

credits 1.22.2

Simon Michael, Stephen Morgan.

2021-08-02 hledger-1.22.1

hledger 1.22.1

Improvements

  • Bash shell completions (for hledger, hledger-ui, hledger-web) are now included in the hledger package's release tarballs, making them more likely to be installed by system packages. (Jakob Schöttl)

  • roi docs now discuss how to quote multi-word queries. (#1609, Dmitry Astapov)

  • Allow megaparsec 9.1

Fixes

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

Improvements

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

  • Allow megaparsec 9.1.

Fixes

  • Up/down keys work on the transaction screen again (broken since 1.22). (#1607, Stephen Morgan)

  • Fix a possible off-by-one bug with valuation date when using 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

Improvements

  • deps: Allow megaparsec 9.1.

Fixes

  • The register chart works again when there are multiple commodities and transaction prices (broken since 1.22). (#1597, Stephen Morgan)

credits 1.22.1

Simon Michael, Stephen Morgan, Jakob Schöttl, Dmitry Astapov.

2021-07-03 hledger-1.22

Optimisations, bugfixes. (announcement)

project changes 1.22

Software:

  • We now provide static executables for GNU/Linux on x64 (amd64) and arm32v7 architectures. These are more portable and more likely to work on your linux system than the dynamic Ubuntu executables we have been providing. These will also be useful for Nextcloud.com users. (#1571) (Garret McGraw)

  • GHC 9.0 support has been added. We have dropped official support for GHC 8.0/8.2/8.4; building hledger now requires GHC 8.6 or newer.

Docs:

  • The info manuals now have the proper metadata so you or your packager can install them with install-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)

Chat:

  • The hledger IRC channels ( #hledger:libera.chat, #hledger-bots:libera.chat ) moved to Libera.chat.

  • The hledger Matrix room ( #hledger:matrix.org ), is now on at least equal "official" footing with the IRC channel.

  • I upgraded the matrix room to a newer version of the Matrix protocol. This effectively splits it into an old (read only) room and a new room. If you are joined to the old room, you might not have noticed; in your matrix client, please follow the link to the new room, ie #hledger:matrix.org.

  • I briefly bridged the IRC and matrix rooms, because having two chats (four if we consider #plaintextaccounting) is a pain. I hope to try the experiment again at some point.

hledger 1.22

Features

  • check: A new 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)

Improvements

  • Many internal optimisations have been applied (cf hledger-lib changelog). Overall, you can expect most reports to be about 20% faster. The register report is more than 2x faster and uses 4x less memory. (Stephen Morgan)

    ~/src/hledger$ quickbench -w hledger-1.21,hledger
    Running 5 tests 1 times with 2 executables at 2021-06-29 13:13:26 HST:
    
    Best times:
    +----------------------------------------------------++--------------+---------+
    |                                                    || hledger-1.21 | hledger |
    +====================================================++==============+=========+
    | -f examples/10000x1000x10.journal print            ||         1.18 |    0.90 |
    | -f examples/10000x1000x10.journal register         ||        12.82 |    5.95 |
    | -f examples/10000x1000x10.journal balance          ||         1.38 |    0.86 |
    | -f examples/1000x1000x10.journal balance --weekly  ||         0.96 |    0.78 |
    | -f examples/10000x1000x10.journal balance --weekly ||        13.07 |   10.79 |
    +----------------------------------------------------++--------------+---------+
    
  • ANSI color is now disabled automatically (on stdout) when the -o/--output-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)

Fixes

  • 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

Improvements

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

Fixes

  • 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

Improvements

  • The --version flag shows more detail (git tag/patchlevel/commit hash, platform/architecture). (Stephen Morgan)

  • Allow yesod-form 1.7 (Felix Yan)

  • Add now-required lower bound on containers. (#1514)

  • GHC 9.0 is now officially supported. GHC 8.0, 8.2, 8.4 are no longer supported; we now require GHC 8.6 or greater.

Fixes

  • In the add form, fix a bug where extra posting rows were not added when needed in certain web browsers. (charukiewicz)

credits 1.22

This release was brought to you by Simon Michael, Stephen Morgan, Felix Yan, crocket, Eric Mertens, Damien Cassou, charukiewicz, and Garret McGraw.

2021-03-10 hledger-1.21

More speed; more cli-accessible docs; value change report; improvements to balance reports, valuation and more (announcement)

project-wide changes 1.21

  • roi has a new cookbook doc, and example files have been updated. (Dmitry Astapov)

  • Example CSV rules for the Daedalus wallet have been added.

  • The default stackage resolver/GHC version has been bumped to lts-17.4/ghc-8.10.4.

  • tools/generatejournal now includes more commodities and prices in generated journals. (Stephen Morgan)

  • Our functional tests now also run on BSD. (#1434, Felix Van der Jeugt)

  • Addon scripts in bin/ have been updated for latest hledger API (Stephen Morgan).

  • Addon scripts are now compiled as part of our CI tests, and always with the same version of hledger source they were shipped with. We now require script users to check out the hledger source tree and run the scripts (or, bin/compile.sh) from there. This keeps users and tests in sync, making things more reliable for everyone. (#1453)

  • Last but not least, hledger's bash completions (provided in ./shell-completions/) have been thoroughly updated (#1404, #1410, Vladimir Zhelezov).

hledger cli 1.21

general

  • hledger is now generally about 10% more memory- and time-efficient, and significantly more so in certain cases, eg journals with many total transaction prices. (Stephen Morgan)

  • The --help/-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.

valuation

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

commands

  • add: Infix matches are now scored higher. If the search pattern occurs in full within the other description, that match gets a +0.5 score boost.

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

general

  • strict mode: with -s/--strict, hledger requires that all accounts and commodities are declared with directives.

  • Reverted a stripAnsi change in 1.19.1 that caused a 3x slowdown of amount rendering in terminal reports. (#1350)

  • Amount and table rendering has been improved, so that stripAnsi is no longer needed. This speeds up amount rendering in the terminal, speeding up some reports by 10% or more since 1.19. (Stephen Morgan)

  • Amount eliding no longer displays corrupted ANSI codes (#1352, Stephen Morgan)

  • Eliding of multicommodity amounts now makes better use of available space, avoiding unnecessary eliding (showing as many amounts as possible within 32 characters). (Stephen Morgan)

  • Command line help for --no-elide now mentions that it also disables eliding of multicommodity amounts.

  • Query terms containing quotes (eg to match account names containing quotes) now work properly. (#1368, Stephen Morgan)

  • cli, journal: Date range parsing is more robust, fixing failing/incorrect cases such as: (Stephen Morgan)

    • a hyphenated range with just years (2017-2018)
    • a hyphenated date with no day in a hyphenated range (2017-07-2018)
    • a dotted date with no day in a dotted range (2017.07..2018.02)
  • Debug output is prettier (eg, in colour), using pretty-simple instead of pretty-show.

  • csv, timedot, timeclock files now respect command line --alias options, like journal files. (#859)

  • Market price lookup for value reports is now more robust, fixing several bugs (and debug output is more informative). There has been a slight change in functionality: when chaining prices, we now prefer chains of all "forward" prices, even if longer, with chains involving reverse prices being the last resort. (#1402)

commands

  • add: number style (eg thousands separators) no longer disturbs the value that is offered as default. (#1378)

  • bal: --invert now affects -S/--sort-amount, reversing the order. (#1283, #1379) (Stephen Morgan)

  • bal: --budget reports no longer insert an extra space inside the brackets. (Stephen Morgan)

  • bal: --budget reports now support CSV output (#1155)

  • bal, is, bs --change: Valued multiperiod balance change reports now show changes of value, rather than the value of changes. (#1353, Stephen Morgan)

  • bal: clearer debug output, following debug levels policy

  • check: A new command which consolidating the various check-* commands. It runs the default, strict, or specified checks and produces no output and a zero exit code if all is well.

  • check-dates: this command is deprecated and will be removed in next release; use "hledger check ordereddates" instead.

  • check-dupes: this command is deprecated and will be removed in next release; use "hledger check uniqueleafnames" instead.

  • import: The journal's commodity styles (declared or inferred) are now applied to imported amounts, overriding their original number format.

  • roi: TWR now handles same-day pnl changes and cashflows, calculation failure messages have been improved, and the documentation includes more detail and examples. (#1398) (Dmitry Astapov)

journal format

  • The journal's commodity styles are now applied to forecasted transactions. (#1371)

  • journal, csv: commodity style is now inferred from the first amount, as documented, not the last. This was "working wrongly" since hledger 1.12..

  • A zero market price no longer causes "Ratio has zero denominator" error in valued reports. (#1373)

csv format

  • The new decimal-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

new

  • aregister: a new command showing a transaction-oriented register for a single account. This is like hledger-ui, hledger-web, or your bank statement, and unlike the register command which shows individual postings possibly spanning multiple accounts. You might prefer aregister when reconciling real-world asset/liability accounts, and register when reviewing detailed revenues/expenses. (#1294)

  • codes: a new command for listing transaction codes

  • print: a new 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)

improved

  • Reports involving multiple commodities now show at most two commodities per amount by default, making multicolumn reports less wide and more readable. Use the --no-elide flag to prevent this.

  • Flat (AKA list) mode is now the consistent default used by all balance reports and other commands showing accounts. (Stephen Morgan)

  • All commands supporting tree/list mode now accept -t and -l as short forms of the --tree and --flat flags. (#1286)

  • account,bal,bs,cf,is: --drop now also works in tree mode (Stephen Morgan)

  • bal,bs,cf,is: tabular balance reports now elide (compress) boring parent accounts, like the non-tabular reports. (Stephen Morgan)

  • bal,bs,cf,is: monthly column headings are no longer be displayed as just the month abbreviations, if multiple years are being displayed.

  • bal --budget: with --cumulative or --historical, column headings now correctly show the period end dates rather than date spans.

  • bs,cf,is: --no-total now hides subtotals as well as the grand total (Stephen Morgan)

  • bs,cf,is: -%/--percent no longer implies --no-total. (Stephen Morgan)

  • roi: errors are now shown without a call stack

  • tags: the new --parsed flag causes all tags or values to be shown, including duplicates, in the order they were parsed. Blank/empty values are omitted by default and can be shown with -E/--empty.

  • Debug output is now organised better by debug level. The levels are:

    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

correctness/robustness

  • Added a missing lower bound for aeson, making cabal installs more reliable. (#1268)

  • When parsing dates, we now require the year to have at least four digits. So eg Feb 1 in the year 10 would need to be written 0010-02-01, 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

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

commands

  • add: you can use < to undo and redo previous inputs (Gaith Hallak)

  • bs, cf, is, bal, print, reg: support json output

  • bs, cf, is: fix excess subreport columns in csv output

  • bs, cf, is, bal: fix an issue with border intersections in --pretty-tables output. (Eric Mertens)

  • close: fix a rounding bug that could generate unbalanced transactions. (#1164)

  • close: hide cost prices by default, show them with --show-costs. close no longer preserves costs (transaction prices) unless you ask it to, since that can generate huge entries when there are many foreign currency/investment transactions. (#1165)

  • close: equity amounts are omitted by default, for simpler entries; -x/--explicit shows them (usually causing more postings). (#1165)

  • close: --interleaved generates equity postings alongside each closed account, making troubleshooting easier.

  • close: "equity:opening/closing balances" is now the default closing and opening account.

  • close: --close-desc/--open-desc customise the closing/opening transaction descriptions. (#1165)

  • close: some --open*/--close* flags have been simplified for memorability:

    --closing -> --close
    --opening -> --open
    --close-to -> --close-acct
    --open-from -> --open-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.

commands

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

commands

  • add, web: on Windows, trying to add transactions to a file path containing trailing periods (eg hledger add -f Documents.\.hledger.journal) now gives an error, since this could cause data loss otherwise (#1056). This affects the add command and hledger-web's add form.

  • bal: --budget: don't always convert to cost.

  • bal: --budget: don't show a percentage when budgeted and actual amounts are in different commodities.

  • bal/bs/bse: -H/--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:

    /accountnames
    /transactions
    /prices
    /commodities
    /accounts
    /accounttransactions/ACCT
    

    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 http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @in.json; echo
    

    (#316)

  • fix unbalanced transaction prevention in the add form

  • fix transaction-showing tooltips (#927)

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

Developers:

  • developer docs have moved from the wiki into CONTRIBUTING.md (#920)

  • new streamlined changelog update process. Shake targets:

    ./Shake changelogs
    ./Shake CHANGES.md
    ./Shake CHANGES.md-dry
    ./Shake PKG/CHANGES.md
    ./Shake PKG/CHANGES.md-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

    changed:

    • amultiplier -> aismultiplier

    • Amount fields reordered for clearer debug output

    • tpreceding_comment_lines -> tprecedingcomment, reordered

    • Hledger.Data.TransactionModifier.transactionModifierToFunction -> modifyTransactions

    • Hledger.Read.Common.applyTransactionModifiers -> Hledger.Data.Journal.journalModifyTransactions

    • HelpTemplate -> CommandDoc

credits 1.13

Release contributors: Simon Michael, Jakob Schöttl, Dmitry Astapov.

2018/12/02 hledger 1.12

Account type declarations, complete balance assertions, GHC 8.6 support, hledger-ui usability updates, misc fixes (mail)

hledger cli 1.12

  • install script: ensure a new-enough version of stack; more informative output

  • build with GHC 8.6/base-4.12 (Peter Simons)

  • add required upper bound for statistics (Samuel May)

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

    1/1
      (a)  A 1
      (a)  B 1
      (a)  0   =  A 1   ; commodity A balance assertion, succeeds
      (a)  0   == A 1   ; complete balance assertion, fails
    
  • journal format: account directives now allow whitespace or a comment after the account name

  • journal format: using ~ for home directory in include directives now works (#896) (Mykola Orliuk)

  • journal format: prevent misleading parse error messages with cyclic include directives (#853) (Alex Chen)

  • journal format: transaction modifier multipliers handle total-priced amounts correctly (#928).
    Multipliers (*N) in transaction modifier rules did not multiply total-priced amounts properly. Now the total prices are also multiplied, keeping the transaction balanced.

  • journal format: do amount inference/balance assignments/assertions before transaction modifiers (#893, #908) (Jesse Rosenthal)
    Previously, transaction modifier (auto postings) rules were applied before missing amounts were inferred. This meant amount multipliers could generate too many missing-amount postings, making the transaction unbalanceable (#893).

    Now, missing amount inference (and balance assignments, and balance assertions, which are interdependent) are done earlier, before transaction modifier rules are applied (#900, #903).

    Also, we now disallow the combination of balance assignments and transaction modifier rules which both affect the same account, which could otherwise cause confusing balance assertion failures (#912). (Because assignments now generate amounts to satisfy balance assertions before transaction modifier rules are applied (#908).)

  • journal format: periodic transaction rules are now aware of Y default year directives. (#892)
    Ie when a default year Y is in effect, they resolve partial or relative dates using Y/1/1 as the reference date, rather than today's date.

hledger-ui 1.12

  • fix "Any" build error with GHC < 8.4

  • error screen: always show error position properly (#904) (Mykola Orliuk)

  • accounts screen: show correct balances when there's only periodic transactions

  • drop the --status-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 0.3.0.1+

  • uses hledger-lib 1.10

hledger-web 1.10

  • view, add, edit permissions can be set at CLI or by Sandstorm HTTP header

  • the edit form has been revived, for whole-journal editing

  • the journal can now be uploaded and downloaded

  • the e key toggles empty accounts in the sidebar

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

(mail)

Release contributors: Simon Michael, Dmitry Astapov, Mykola Orliuk, Eli Flanagan, Elijah Caine, Sam Jeeves, Matthias Kauer, Hans-Peter Deifel, Mick Dekkers, Nadrieril, Alvaro Fernando García.

project-wide changes 1.5

  • remove upper bounds on all but hledger* and base (experimental) It's rare that my deps break their api or that newer versions must be avoided, and very common that they release new versions which I must tediously and promptly test and release hackage revisions for or risk falling out of stackage. Trying it this way for a bit.

hledger-lib 1.5

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

Tools

make ghci-prof starts GHCI in profiling mode, enabling stack traces with traceStack

make ghci-web now also creates required symlinks

make site-reload opens an auto-reloading browser on the latest site html

make changelog-draft shows the commits since last tag as org nodes

hledger-lib 1.3

journal format

The "uncleared" transaction/posting status (and associated UI flags and keys) has been renamed to "unmarked" to remove ambiguity and confusion. See the issue and linked mail list discussion for more background. (#564)

csv format

In CSV conversion rules, assigning to the "balance" field name creates balance assertions (#537, Dmitry Astapov).

Doubled minus signs are handled more robustly (fixes #524, Nicolas Wavrant, Simon Michael)

Misc

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

Deps: allow megaparsec 5.3.

hledger cli 1.3

CLI

The "uncleared" transaction/posting status, and associated UI flags and keys, have been renamed to "unmarked" to remove ambiguity and confusion. This means that we have dropped the --uncleared flag, and our -U flag now matches only unmarked things and not pending ones. See the issue and linked mail list discussion for more background. (#564)

Also the -P short flag has been added for --pending, and the -U/-P/-C flags can be combined.

bs/is: fix "Ratio has zero denominator" error (#535)

bs/is/cf: fix --flat (#552) (Justin Le, Simon Michael)

bal/bs/is/cf: show negative amounts in red (Simon Michael, Justin Le). These commands now show negative amounts in red, when hledger detects that ANSI codes are supported, (ie when TERM is not "dumb" and stdout is not being redirected or piped).

print: show pending mark on postings (fixes #563). A pending mark on postings is now displayed, just like a cleared mark. Also there will now be a space between the mark and account name.

print: amounts are now better aligned, eg when there are posting status marks or virtual postings.

Addons

prices: add --inverted-costs flag, sort output, increase precision (Mykola Orliuk)

rewrite: add support for rewriting multipler postings into different commodities. For example, postings in hours can be used to generate postings in USD. (#557) (Christian G. Warden)

make addons compiles the experimental add-ons.

hledger-ui 1.3

The register screen now shows transaction status marks.

The "uncleared" status, and associated UI flags and keys, have been renamed to "unmarked" to remove ambiguity and confusion. This means that we have dropped the --uncleared flag, and our -U flag now matches only unmarked things and not pending ones. See the issue and linked mail list discussion for more background. (#564)

The P key toggles pending mode, consistent with U (unmarked) and C (cleared). There is also a temporary --status-toggles flag for testing other toggle styles; see hledger-ui -h. (#564)

There is now less "warping" of selection when lists change:

  • When the selected account disappears, eg when toggling zero accounts, the selection moves to the alphabetically preceding item, instead of the first one.

  • When the selected transaction disappears, eg when toggling status filters, the selection moves to the nearest transaction by date (and if several have the same date, by journal order), instead of the last one.

In the accounts and register screens, you can now scroll down further so that the last item need not always be shown at the bottom of the screen. And we now try to show the selected item centered in the following situations:

  • after moving to the end with Page down/End
  • after toggling filters/display modes (status, real, historical..)
  • on pressing the control-l key (this forces a screen redraw, also)
  • on entering the register screen from the accounts screen (except the first time, a known problem).

Items near the top won't be centered because we don't scroll above the top of the list.

Emacs movement keys are now supported, as well as VI keys. CTRL-b/CTRL-f/CTRL-n/CTRL-p and hjkl should work wherever unmodified arrow keys work.

In the transaction screen, amounts are now better aligned, eg when there are posting status marks or virtual postings.

Deps: allow brick 0.19 (#575, Felix Yan, Simon Michael)

hledger-web 1.3

Depends on hledger 1.3.

hledger-api 1.3

Depends on hledger 1.3.

2017/3/31 hledger 1.2

new commands list, more powerful balancesheet/incomestatement/cashflow commands, more parseable print output, better --pivot, basic automated postings and periodic transactions support, more and easier addons, bugfixes

Release contributors: Simon Michael, Mykola Orliuk, Justin Le, Peter Simons, Stefano Rodighiero, Moritz Kiefer, Pia Mancini, Bryan Richter, Steven R. Baker, Hans-Peter Deifel, Joshua Chia, Joshua Kehn, Michael Walker.

project-wide changes 1.2

Packaging

bump stack config to latest lts, bump brick to 0.15.2 to allow hledger-iadd install in hledger dir, update cabal files to latest hpack 0.17.0/stack 1.4 format (#512), use more accurate license tag in Cabal file (Peter Simons).

Finance

set up a hledger open collective (http://opencollective.com/hledger), more devguide links to issues with bounties, codefund link, start tracking and publishing project finances (dogfooding!).

Documentation and website

docs page & manual cleanups, begin organising a cookbook, update addons list, move detailed addon docs out of hledger manual, document addons installation, explain print's CSV output, note an issue with balance assertions & multiple -f options, clarify tags, add github stars widget to home and devguide, improve market price docs, ui & web screenshots layout fixes, fix extra whitespace after synopsis in hledger-web text manuals, update accounts directive/budget/rewrite/read-related mockups, drop old org notes.

Examples

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

Tools

Travis CI now checks functional tests/build warnings/addons, temporary workaround for Appveyor CI failures, remove accidentally committed pandoc executables, some pandoc filter fixes, mailmap file to clean up git log authors, bench.hs cleanup, fix gitignore of generated manuals, avoid excessive rebuilding with make [func]test, run functional tests more verbosely, add alex/happy update step to cabal-install.sh.

hledger-lib 1.2

journal format

A pipe character can optionally be used to delimit payee names in transaction descriptions, for more accurate querying and pivoting by payee. Eg, for a description like payee name | additional notes, the two parts will be accessible as pseudo-fields/tags named payee and note.

Some journal parse errors now show the range of lines involved, not just the first.

ledger format

The experimental ledger: reader based on the WIP ledger4 project has been disabled, reducing build dependencies.

Misc

Fix a bug when tying the knot between postings and their parent transaction, reducing memory usage by about 10% (#483) (Mykola Orliuk)

Fix a few spaceleaks (#413) (Moritz Kiefer)

Add Ledger.Parse.Text to package.yaml, fixing a potential build failure.

Allow megaparsec 5.2 (#503)

Rename optserror -> usageError, consolidate with other error functions

hledger cli 1.2

CLI

"hledger" and "hledger -h" now print a better organised commands list and general usage message respectively (#297).

The common reporting flags can now be used anywhere on the command line.

Fixed deduplication of addons in commands list.

Fixed ugly stack traces in command line parse error messages.

The -V/--value flag is now a global report flag, so it works with balance, print, register, balancesheet, incomestatement, cashflow, etc. (Justin Le)

The --pivot global reporting option replaces all account names with the value of some other field or tag. It has been improved, eg:

  • we don't add the field/tag name name as a prefix
  • when pivoting on a tag, if the tag is missing we show a blank (rather than showing mixed tag values and account names)
  • a pipe character delimiter may be used in descriptions to get a more accurate and useful payee report (hledger balance --pivot payee)

options cleanups

Addons

Easier installation: move add-ons and example scripts to bin/, convert to stack scripts, add a build script to install all deps, add some functional tests, test add-ons with Travis CI, add installation docs to download page.

Improved docs: all addons now contain their own documentation. Most of them (all but hledger-budget) use a new reduced-boilerplate declaration format and can show short (-h) and long (--help) command line help. (Long help is declared with pre and postambles to the generated options help, short help is that truncated at the start of the hledger common flags.)

hledger now shows a cleaner list of addon commands, showing only the compiled version of an addon when both source and compiled versions are in $PATH. (Addons with .exe extension or no extension are considered compiled. Modification time is not checked, ie, an old compiled addon will override a newer source version. If there are three or more versions of an addon, all are shown. )

New addons added/included:

  • autosync - example symlink to ledger-autosync
  • budget - experimental budget reporting command supporting Ledger-like periodic transactions and automated transactions (Mykola Orliuk)
  • chart - pie-chart-generating prototype, a repackaging of the old hledger-chart tool
  • check - more powerful balance assertions (Michael Walker)
  • check-dupes - find accounts sharing the same leaf name (Stefano Rodighiero)
  • prices - show all market price records (Mykola Orliuk)
  • register-match - a helper for ledger-autosync's deduplication, finds best match for a transaction description

The equity command now always generates a valid journal transaction, handles prices better, and adds balance assertions (Mykola Orliuk).

The rewrite command is more robust and powerful (Mykola Orliuk):

  • in addition to command-line rewrite options, it understands rewrite rules defined in the journal, similar to Ledger's automated transactions (#99). Eg:

    = ^income
        (liabilities:tax)  *.33
    
    = expenses:gifts
        budget:gifts  *-1
        assets:budget  *1
    
  • it can generate diff output, allowing easier review of the proposed changes, and safe modification of original journal files (preserving file-level comments and directives). Eg:

    hledger-rewrite --diff Agency --add-posting 'Expenses:Taxes  *0.17' | patch
    
  • rewrites can affect multiple postings in a transaction, not just one.

  • posting-specific dates are handled better

balance

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

balancesheet/cashflow/incomestatement

These commands are now more powerful, able to show multicolumn reports and generally having the same features as the balance command. (Justin Le)

balancesheet has always ignored a begin date specified with a -b or -p option; now it also ignores a begin date specified with a date: query. (Related discussion at #531)

print

The output of print is now always a valid journal (fixes #465) (Mykola Orliuk).

print now tries to preserves the format of implicit/explicit balancing amounts and prices, by default. To print with all amounts explicit, use the new --explicit/-x flag (fixes #442). (Mykola Orliuk)

Don't lose the commodity of zero amounts/zero balance assertions (fixes #475) (Mykola Orliuk)

Misc

Fix a regression in the readability of option parsing errors (#478) (Hans-Peter Deifel)

Fix an example in Cli/Main.hs (Steven R. Baker)

Allow megaparsec 5.2 (#503)

hledger-ui 1.2

Fix a pattern match failure when pressing E on the transaction screen (fixes #508)

Accounts with ? in name had empty registers (fixes #498) (Bryan Richter)

Allow brick 0.16 (Joshua Chia) and brick 0.17/vty 0.15 (Peter Simons)

Allow megaparsec 5.2 (fixes #503)

Allow text-zipper 0.10

hledger-web 1.2

Accounts with ? in name had empty registers (fixes #498) (Bryan Richter)

Allow megaparsec 5.2 (fixes #503)

2016/12/31 hledger 1.1

more robust file format detection, integration of WIP ledger4 parser, balance assignments, hledger-ui --watch, hledger-iadd integration, bugfixes

Release contributors: Simon Michael, Johannes Gerer, Mykola Orliuk, Shubham Lagwankar.

project-wide changes 1.1

misc

  • don't show stack trace details in errors

  • more predictable file format detection

    When we don't recognise a file's extension, instead of choosing a subset of readers to try based on content sniffing, now we just try them all. Also, this can be overridden by prepending the reader name and a colon to the file path (eg timedot:file.dat, csv:-).

  • avoid creating junk CSV rules files when trying alternate readers. We now create it only after successfully reading a file as CSV.

  • improvements to -B and -V docs: clearer descriptions, more linkage (#403)

hledger-lib 1.1

journal format

  • balance assignments are now supported (#438, #129, #157, #288)

    This feature also brings a slight performance drop (~5%); optimisations welcome.

  • also recognise *.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.

misc

  • update base lower bound to enforce GHC 7.10+

    hledger-lib had a valid install plan with GHC 7.8, but currently requires GHC 7.10 to compile. Now we require base 4.8+ everywhere to ensure the right GHC version at the start.

  • Hledger.Read api cleanups

  • rename dbgIO to dbg0IO, consistent with dbg0, and document a bug in dbg*IO

  • make readJournalFiles [f] equivalent to readJournalFile f (#437)

  • more general parser types enabling reuse outside of IO (#439)

hledger cli 1.1

balance

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

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

misc

  • fix non-existent "oldtime" dependency (#431)

  • hledger-equity.hs now generates valid journal format when there are multiple commodities

hledger-ui 1.1

  • with --watch, the display updates automatically to show file or date 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 127.0.0.1 being hardcoded. That can now be changed with --host=IPADDR. Also, the default base url uses this address rather than a hard-coded "localhost".

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

(mail)

Release contributors: Simon Michael, Dominik Süß, Thomas R. Koll, Moritz Kiefer, jungle-boogie, Sergei Trofimovich, Malte Brandy, Sam Doshi, Mitchell Rosen, Hans-Peter Deifel, Brian Scott, and Andrew Jones.

misc

  • added GHC 8 support, dropped GHC 7.6 and 7.8 support.

    GHC 7.8 support could be restored with small code changes and a maintainer.

  • a cabal.project file has been added (Moritz Kiefer)

  • use hpack for maintaining cabal files (#371).

    Instead of editing cabal files directly, we now edit the less verbose and less redundant package.yaml files and let stack (or hpack) update the cabal files. We commit both the .yaml and .cabal files.

  • clean up some old cabal flags

  • tools/simplebench has been spun off as the quickbench package.

  • add Appveyor CI builds, provide more up-to-date Windows binaries

  • extra: add a bunch of CSV rules examples

docs

  • the website is simpler, clearer, and more mobile-friendly.

    Docs are now collected on a single page and organised by type: getting started, reference, more.

  • reference docs have been split into one manual for each executable and file format.

    This helps with maintenance and packaging and also should make it easier to see what's available and to read just what you need.

  • manuals are now provided in html, plain text, man and info formats

    generated from the same source by a new Shake-based docs build system. (#292)

  • versioned manuals are provided on the website, covering recent releases and the latest dev version (#385, #387)

  • manuals are built in to the hledger executables, allowing easy offline reading on all platforms.

    PROG -h              shows PROG's command-line usage
    PROG --help          shows PROG's manual (fixed width)
    PROG --man           shows PROG's manual with man (formatted/paged)
    PROG --info          shows PROG's manual with info (hypertext)
    hledger help [TOPIC] shows any manual
    hledger man  [TOPIC] shows any manual with man
    hledger info [TOPIC] shows any manual with info
    
  • the general and reporting options are now listed in all executable manuals.

    We assume any of them which are unsupported are harmlessly ignored.

  • demo.hledger.org is using beancount's example journal.

    This is the somewhat realistic example journal from the beancount project, tweaked for hledger.

  • minor copyedits (jungle-boogie)

cli

  • parsing multiple input files is now robust.

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

misc

  • file parsers have been ported from Parsec to Megaparsec \o/ (#289, #366) (Alexey Shmalko, Moritz Kiefer)

  • most hledger types have been converted from String to Text, reducing memory usage by 30%+ on large files

  • file parsers have been simplified for easier troubleshooting (#275).

    The journal/timeclock/timedot parsers, instead of constructing opaque journal update functions which are later applied to build the journal, now construct the journal directly by modifying the parser state. This is easier to understand and debug. It also rules out the possibility of journal updates being a space leak. (They weren't, in fact this change increased memory usage slightly, but that has been addressed in other ways). The ParsedJournal type alias has been added to distinguish "being-parsed" journals and "finalised" journals.

  • file format detection is more robust.

    The Journal, Timelog and Timedot readers' detectors now check each line in the sample data, not just the first one. I think the sample data is only about 30 chars right now, but even so this fixed a format detection issue I was seeing. Also, we now always try parsing stdin as journal format (not just sometimes).

  • all file formats now produce transaction ids, not just journal (#394)

  • git clone of the hledger repo on windows now works (#345)

  • added missing benchmark file (#342)

  • our stack.yaml files are more compatible across stack versions (#300)

  • use newer file-embed to fix ghci working directory dependence

  • report more accurate dates in account transaction report when postings have their own dates

    (affects hledger-ui and hledger-web registers). The newly-named "transaction register date" is the date to be displayed for that transaction in a transaction register, for some current account and filter query. It is either the transaction date from the journal ("transaction general date"), or if postings to the current account and matched by the register's filter query have their own dates, the earliest of those posting dates.

  • simplify account transactions report's running total.

    The account transactions report used for hledger-ui and -web registers now gives either the "period total" or "historical total", depending strictly on the --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

add

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

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

balance

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

balancesheet

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

register

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

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

stats

  • fixed an issue with ordering of include files

misc

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

misc

  • H toggles between showing "historical" or "period" balances (#392).

    By default hledger-ui now shows historical balances, which include transactions before the report start date (like hledger balance --historical). Use the H key to toggle to "period" mode, where balances start from 0 on the report start date.

  • shift arrow keys allow quick period browsing

    • shift-down narrows to the next smaller standard period (year/quarter/month/week/day), shift-up does the reverse
    • when narrowed to a standard period, shift-right/left moves to the next/previous period
    • `t` sets the period to today.
  • a runs the add command

  • E runs $HLEDGERUIEDITOR or $EDITOR or a default editor (vi) on the journal file.

    When using emacs or vi, if a transaction is selected the cursor will be positioned at its journal entry.

  • / key sets the filter query; BACKSPACE/DELETE clears it

  • Z toggles display of zero items (like --empty), and they are shown by default.

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

ui

  • use full width on large screens, hide sidebar on small screens, more standard bootstrap styling (#418, #422) (Dominik Süß)

  • show the sidebar by default (#310)

  • fix the add link's tooltip

  • when the add form opens, focus the first field (#338)

  • leave the add form's date field blank, avoiding a problem with tab clearing it (#322)

  • use transaction id instead of date in transaction urls (#308) (Thomas R. Koll)

  • after following a link to a transaction, highlight it (Thomas R. Koll)

  • misc. HTML/CSS/file cleanups/fixes (Thomas R. Koll)

misc

  • startup is more robust (#226).

    Now we exit if something is already using the specified port, and we don't open a browser page before the app is ready.

  • termination is more robust, avoiding stray background threads.

    We terminate the server thread more carefully on exit, eg on control-C in GHCI.

  • more robust register dates and filtering in some situations (see hledger-ui notes)

  • reloading the journal preserves options, arguments in effect (#314).

    The initial query specified by command line arguments is now preserved when the journal is reloaded. This does not appear in the web UI, it's like an invisible extra filter.

  • show a proper not found page on 404

  • document the special `inacct:` query (#390)

hledger-api 1.0

misc

  • new hledger-api tool: a simple web API server with example clients (#316)

  • start an Angular-based API example client (#316) (Thomas R. Koll)

2008-2015 Pre-1.0

2015/10/30 hledger 0.27

New curses-style interface, market value reporting, wide characters, fast regex aliases, man pages (mail)

Release contributors: Simon Michael, Carlos Lopez-Camey.

hledger 0.27:

Account aliases:

  • Regular expression account aliases are now fast enough that you can use lots of them without slowing things down. They now take O(aliases x accounts) time, instead of O(aliases x transactions); also, regular expressions are no longer recompiled unnecessarily.

Documentation:

  • The hledger packages now have man pages, based on the current user manual, thanks to the mighty pandoc (#282).

Journal format:

  • Dates must now begin with a digit (not /, eg).

  • The comment directive longer requires an end comment, and will extend to the end of the file(s) without it.

Command-line interface:

  • Output (balance reports, register reports, print output etc.) containing wide characters, eg chinese/japanese/korean characters, should now align correctly, when viewed in apps and fonts that show wide characters as double width (#242).

  • The argument for --depth or depth: must now be positive.

add:

  • Journal entries are now saved with all amounts explicit, to avoid losing price info (#283).

  • Fixed a bug which sometimes (when the same letter pair was repeated) caused it not to pick the most similar past transaction for defaults.

balance:

  • There is now a -V/--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.

print:

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

register:

  • Amount columns now resize automatically, using more space if it's needed and available.

hledger-ui 0.27:

  • hledger-ui is a new curses-style UI, intended to be a standard part of the hledger toolset for all users (except on native MS Windows, where the vty lib is not yet supported).

    The UI is quite simple, allowing just browsing of accounts and transactions, but it has a number of improvements over the old hledger-vty, which it replaces:

    • adapts to screen size
    • handles wide characters
    • shows multi-commodity amounts on one line
    • manages cursor and scroll position better
    • allows depth adjustment
    • allows --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.

Queries:

  • real: (no argument) is now a synonym for real:1

  • tag: now matches tag names with a regular expression, like most other queries

  • empty: is no longer supported, as it overlaps a bit confusingly with amt:0. The --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:

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

add:

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

balancesheet:

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

print:

  • We now limit the display precision of inferred prices. (#262)

    When a transaction posts to two commodities without specifying the conversion price, we generate a price which makes it balance (cf <http://hledger.org/journal.html#transaction-prices). The print command showed this with full precision (so that manual calculations with the displayed numbers would look right), but this sometimes meant we showed 255 digits (when there are multiple postings in the commodity being priced, and the averaged unit price is an irrational number). In this case we now set the price's display precision to the sum of the (max) display precisions of the commodities involved. An example:

    hledger -f- print
    <<<
    1/1
        c    C 10.00
        c    C 11.00
        d  D -320.00
    >>>
    2015/01/01
        c  C 10.00 @ D 15.2381
        c  C 11.00 @ D 15.2381
        d     D -320.00
    
    >>>=0
    

    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.

register:

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

stats:

  • stats now supports -o/--outputfile, like register/balance/print.
  • An O(n^2) performance slowdown has been fixed, it's now much faster on large journals.
    +--------------------------------------++--------+--------+
    |                                      ||   0.25 |   0.26 |
    +======================================++========+========+
    | -f data/100x100x10.journal     stats ||   0.10 |   0.16 |
    | -f data/1000x1000x10.journal   stats ||   0.45 |   0.21 |
    | -f data/10000x1000x10.journal  stats ||  58.92 |   2.16 |
    +--------------------------------------++--------+--------+
    

Miscellaneous:

  • The June 30 day span was not being rendered correctly; fixed. (#272)
  • The deprecated shakespeare-text dependency has been removed more thoroughly.
  • The bench script invoked by "cabal bench" or "stack bench" now runs some simple benchmarks. You can get more accurate benchmark times by running with --criterion. This will usually give much the same numbers and takes much longer. Or with --simplebench, it benchmarks whatever commands are configured in bench/default.bench. This mode uses the first "hledger" executable in $PATH.

User-visible changes in hledger-web since 0.25.1:

  • make the j keybinding respect --base-url (fixes #271)
  • respect command line options (fixes #225)
  • include the unminified jquery source again (#161)
  • fix build breakage from #165 (fixes #268)
  • fix a js error breaking add form in browsers other than firefox (fixes #251)
  • drop deprecated network-conduit dependency
2015/4/29 hledger-web 0.25.1
  • support/require base-compat >0.8 (#245)
2015/4/29 hledger 0.25.1
  • timelog: support the description field (#247)
2015/4/29 hledger-lib 0.25.1
  • support/require base-compat >0.8 (#245)

2015/4/7 hledger 0.25

GHC 7.10 compatibility, terminal width awareness, useful averages and totals columns, and a more robust hledger-web add form.

mail

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
Examples:
    $ hledger reg                 # use terminal width on posix
    $ hledger reg -w 100          # width 100, equal description/account widths
    $ hledger reg -w 100,40       # width 100, wider description
    $ hledger reg -w $COLUMNS,100 # terminal width and set description width
  • balance: new -T/--row-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:

General:

  • fix redundant compilation when cabal installing the hledger packages
  • switch to Decimal for representing amounts (#118)
  • report interval headings (eg in balance, register reports) are shown compactly when possible
  • general speedups.
+--------------------------------------------++----------------+--------------+--------+
|                                            || hledger-0.23.3 | hledger-0.24 | ledger |
+============================================++================+==============+========+
| -f data/100x100x10.journal     balance     ||           0.05 |         0.03 |   0.01 |
| -f data/1000x1000x10.journal   balance     ||           0.34 |         0.21 |   0.04 |
| -f data/10000x1000x10.journal  balance     ||           2.72 |         1.48 |   0.19 |
| -f data/10000x1000x10.journal  balance aa  ||           3.16 |         1.55 |   0.14 |
| -f data/100x100x10.journal     register    ||           0.09 |         0.05 |   0.04 |
| -f data/1000x1000x10.journal   register    ||           0.66 |         0.32 |   0.30 |
| -f data/10000x1000x10.journal  register    ||           6.27 |         2.77 |   2.80 |
| -f data/10000x1000x10.journal  register aa ||           3.30 |         1.62 |   0.21 |
| -f data/100x100x10.journal     print       ||           0.06 |         0.05 |   0.01 |
| -f data/1000x1000x10.journal   print       ||           0.42 |         0.25 |   0.04 |
| -f data/10000x1000x10.journal  print       ||           3.95 |         2.57 |   0.38 |
| -f data/10000x1000x10.journal  print aa    ||           3.23 |         1.56 |   0.14 |
| -f data/100x100x10.journal     stat        ||           0.04 |         0.03 |   0.01 |
| -f data/1000x1000x10.journal   stat        ||           0.35 |         0.24 |   0.03 |
| -f data/10000x1000x10.journal  stat        ||          14.84 |        13.29 |   0.20 |
| -f data/10000x1000x10.journal  stat aa     ||          12.08 |        10.16 |   0.17 |
+--------------------------------------------++----------------+--------------+--------+

Journal format:

  • detect decimal point and digit groups more robustly (#196)
  • check that transaction dates are followed by whitespace or newline
  • check that dates use a consistent separator character
  • balance assertions now are specific to a single commodity, like Ledger (#195)
  • support multi-line comments using "comment", "end comment" directives, like Ledger

CSV format:

  • fix: reading CSV data from stdin now works better
  • the original order of same-day transactions is now usually preserved (if the records appear to be in reverse date order, we reverse them before finally sorting by transaction date)
  • the rules file include directive is now relative to the current file's directory (#198)
  • CSV output is now built in to the balance, print, and register commands, controlled by -O/--output-format (and -o/--output-file, see below). This means that hledger data can be easily exported, eg for spreadsheet reporting or to migrate to a different tool.

CLI:

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

Queries:

  • date:/date2: with a malformed date now reports an error instead of being ignored
  • amt: now supports >= or <=
  • clarify status: docs and behaviour; "*" is no longer a synonym for "1" (fixes #227)

balance:

  • fix: in tree mode, --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

register:

  • runs faster with large output
  • supports date2:, and date:/date2: combined with --date2, better (fixes #201, #221, #222)
  • a depth limit of 0 now shows summary items (see balance)
  • -A/--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:

General:

  • fix: add missing hs/js files to package
  • the web UI has been streamlined, dropping the raw and entries views and the edit form
  • the help dialog has been improved
  • keyboard shortcuts are now available
  • the sidebar can be toggled open or closed (press s)

Journal view:

  • layout tweaks for less truncation of descriptions and account names

Register view:

  • fix: don't show all zero amounts when searching by account within an account register view
  • chart improvements: show zero balances with correct commodity; show accurate balance at all dates; show transaction events & tooltips; show zero/today lines & background colors

Add form:

  • parses data more strictly and gives better errors (eg #194)
  • allows any number of postings, not just two
  • after adding a transaction, goes back to the journal
  • keyboard shortcut (a) allows quick access

Dependencies:

  • allow warp 3*, wai-handler-launch 3*
  • require yesod 1.4* (fixes #212)
  • js updated (jquery, bootstrap, flot), added (typeahead, cookie, hotkeys), removed (select2)

API-ish changes in hledger-lib since 0.23.3:

  • fix combineJournalUpdates folding order
  • fix a regexReplaceCI bug
  • fix a splitAtElement bug with adjacent separators
  • mostly replace slow regexpr with regex-tdfa (fixes #189)
  • use the modern Text.Parsec API
  • allow transformers 0.4*
  • regexReplace now supports backreferences
  • Transactions now remember their parse location in the journal file
  • export Regexp types, disambiguate CsvReader's similarly-named type
  • export failIfInvalidMonth/Day (closes #216)
  • track the commodity of zero amounts when possible (useful eg for hledger-web's multi-commodity charts)
  • show posting dates in debug output
  • more debug helpers
2014/9/12 hledger-web 0.23.3
  • remove warp, wai-handler-launch upper bounds (fixes #205)
2014/9/12 hledger 0.23.3
  • allow text 1.2+ (fixes #207)
2014/5/8 hledger 0.23.2
  • register: also fix date sorting of postings (#184)
2014/5/7 hledger 0.23.1
  • register: fix a refactoring-related regression that the tests missed: if transactions were not ordered by date in the journal, register could include postings before the report start date in the output. (#184)
  • add: don't apply a default commodity to amounts on entry (#138)
  • cli: options before the add-on command name are now also passed to it (#182)
  • csv: allow the first name in a fields list to be empty (#178)
  • csv: don't validate fields count in skipped lines (#177)

2014/5/1 hledger 0.23

command-line fixes and polish, a new accounts command, and a number of changes to the balance command relating to --depth, --flat, and multicolumn mode, which I find has made it much more useful. mail

Changes since 0.22.2:

Journal format:

  • A # (hash) in column 0 is now also supported for starting a top-level journal comment, like Ledger.
  • The "too many missing amounts" error now reminds about the 2-space rule.
  • Fix: . (period) is no longer parsed as a valid amount.
  • Fix: default commodity directives no longer limit the maximum display precision (#169).
  • Fix: + before an amount is no longer parsed as part of the commodity (#181).

CLI:

  • Command-line help cleanups, layout improvements.
  • Descriptions are shown for known add-ons in the command list.
  • Command aliases have been simplified.
  • Add-ons can now have any of these file extensions: none, hs, lhs, pl, py, rb, rkt, sh, bat, com, exe.
  • Add-ons are displayed without their file extensions when possible.
  • Add-ons with the same name as a built-in command or alias are ignored.
  • Fix: add-on detection and invocation now works on windows.
  • Fix: add-ons with digits in the name are now found.
  • Fix: add-on arguments containing a single quote now work.
  • Fix: when -- is used to hide add-on options from the main program, it is no longer passed through as an add-on argument.

accounts:

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

add:

  • Tab completion now works at all prompts, and will insert the default if the input area is empty.
  • Account and amount defaults are more robust and useful.
  • Transactions may also be completed by the enter key, when there are no more default postings.
  • Input prompts are displayed in a different colour when supported.

balance:

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

print:

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

register:

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

Queries:

  • The currency/commodity query prefix (sym:) has been renamed to cur:.
  • Currency/commodity queries are applied more strongly in register and balance reports, filtering out unwanted currencies entirely. Eg hledger balance cur:'$' now reports only the dollar amounts even if there are multi-currency transactions or postings.
  • Amount queries like amt:N, amt:N, where N is not 0, now do an unsigned comparison of the amount and N. That is, they compare the absolute magnitude. To do a signed comparison instead, write N with its sign (eg amt:+N, amt:<+N, amt:>-N).
  • Fix: amount queries no longer give false positives on multi-commodity amounts.

Miscellaneous:

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

New:

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

Improved:

  • web: include unminified source of all javascript to help packagers (fixes #161)
  • web: work around clang-related build failures with OS X mavericks/XCode 5
  • web: allow blaze-html 0.7 (closes #159)
2014/1/6 hledger 0.22.1
  • require the latest pretty-show so hledger installation no longer needs an upgraded version of happy, and the docs build on hackage

  • require regex-tdfa directly instead of regex-compat-tdfa, simplifying Debian packaging

2013/12/13 hledger 0.22

mail

New:

  • balance: with a reporting interval (monthly, yearly etc.), the balance command will now show a multi-column report, showing either the per-period changes in balance (by default), the period ending balances starting from zero (--cumulative), or the actual period ending balances (--historical). A more detailed specification of the balance command's behaviour has been added to Hledger.Cli.Balance.

  • csv: rules files can now include other rules files, useful for factoring out common rules

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

Improved:

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

Fixed:

  • csv: CSV data containing non-ascii characters is now supported

  • build with latest versions of dependencies (text, warp, http-conduit etc.)

Release contributors:

Marko Kocić, Max Bolingbroke, and a big welcome to first-time committer John Wiegley! :)

2013/7/10 hledger-web 0.21.3
  • drop yesod-platform dependency, it is not worthwhile. The other yesod dependencies are currently without version ranges, so cabal install might require --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 0.20.0.1
  • web: require at least version 1.1.7 of yesod-core to avoid a potential build error
  • Update the bug tracker and source repository links on hackage

2013/5/1 hledger 0.20

Bugs fixed:

  • balance: a 0.19 regression which showed wrong total balance with --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

Querying:

  • Use code: to match the transaction code (check number) field
  • Use amt: followed by <, = or > and a number N to match amounts by magnitude. 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

Add-ons:

  • The hledger-interest and hledger-irr commands have been released/updated.
  • hledger-chart and hledger-vty remain unmaintained and deprecated.

Documentation and infrastructure:

  • The hledger docs and website have been reorganised and updated
  • Manuals for past releases are provided as well as the latest dev version
  • hledger has moved from darcs and darcs hub to git and github (!)
  • The bug tracker has moved from google code to github
  • Feature requests and project planning are now managed on trello
  • A build bot builds against multiple GHC versions on each commit

Release contributors:

  • Sascha Welter commissioned register enhancements (--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

mail

  • 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

mail

  • 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

mail

  • 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

mail

  • 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

mail

  • 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

mail

  • 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

mail

  • 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

mail

  • 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

mail

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

Tutorials

Detailed step by step tutorials, with screenshots:

Videos

Manuals

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.

How-tos

Practical advice and examples for real-world tasks:

See also:

Support

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

Advice

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.

Or:

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

Imperfection

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

NAME

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

SYNOPSIS

hledger
hledger COMMAND [OPTS] [ARGS]
hledger ADDONCMD -- [OPTS] [ARGS]

DESCRIPTION

hledger is a robust, user-friendly, cross-platform set of programs for tracking money, time, or any other commodity, using double-entry accounting and a simple, editable file format. hledger is inspired by and largely compatible with ledger(1), and largely interconvertible with beancount(1).

This manual is for hledger's command line interface, version 1.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
  assets:cash

Transactions are dated movements of money (etc.) between two or more accounts: bank accounts, your wallet, revenue/expense categories, people, etc. You can choose any account names you wish, using : to indicate subaccounts. There must be at least two spaces between account name and amount. Positive amounts are inflow to that account (debit), negatives are outflow from it (credit). (Some reports show revenue, liability and equity account balances as negative numbers as a result; this is normal.)

hledger's add command can help you add transactions, or you can install other data entry UIs like hledger-web or hledger-iadd. For more extensive/efficient changes, use a text editor: Emacs + ledger-mode, VIM + vim-ledger, or VS Code + hledger-vscode are some good choices (see https://hledger.org/editors.html).

To get started, run hledger add and follow the prompts, or save some entries like the above in $HOME/.hledger.journal, then try commands like:

$ hledger print -x
$ hledger aregister assets
$ hledger balance
$ hledger balancesheet
$ hledger incomestatement

Run hledger to list the commands. See also the "Starting a journal file" and "Setting opening balances" sections in PART 5: COMMON TASKS.

PART 1: USER INTERFACE

Input

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

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

Commands

hledger provides various subcommands for getting things done. Most of these commands do not change the journal file; they just read it and output a report. A few commands assist with adding data and file management.

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.

Options

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'

or:

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

or:

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

or:

$ hledger ui cur:\\\\$

If you wondered why four backslashes, perhaps this helps:

unescaped:$
escaped:\$
double-escaped:\\$
triple-escaped:\\\\$

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

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:

-txtcsv/tsvhtmljsonsql
aregisterYYYY
balanceY 1Y 1Y 1,2Y
balancesheetY 1Y 1Y 1Y
balancesheetequityY 1Y 1Y 1Y
cashflowY 1Y 1Y 1Y
incomestatementY 1Y 1Y 1Y
printYYYY
registerYYY
  • 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).

Colour

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.

Box-drawing

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.

Paging

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

Environment

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.

PART 2: DATA FORMATS

Journal

hledger's usual data source is a plain text file containing journal entries in hledger journal format. If you're looking for a quick reference, jump ahead to the journal cheatsheet (or use the table of contents at https://hledger.org/hledger.html).

This file represents an accounting General Journal. The .journal file extension is most often used, though not strictly required. The journal file contains a number of transaction entries, each describing a transfer of money (or any commodity) between two or more named accounts, in a simple format readable by both hledger and humans.

hledger's journal format is compatible with most of Ledger's journal format, but not all of it. The differences and interoperation tips are described at hledger and Ledger. With some care, and by avoiding incompatible features, you can keep your hledger journal readable by Ledger and vice versa. This can useful eg for comparing the behaviour of one app against the other.

You can use hledger without learning any more about this file; just use the add or web or import commands to create and update it.

Many users, though, edit the journal file with a text editor, and track changes with a version control system such as git. Editor 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 ;

comment
Or, lines can be enclosed within "comment" / "end comment".
This is a block of 
commented lines.
end comment

# Some journal entries can have semicolon comments at end of line  ; like this
# Some of them require 2 or more spaces before the semicolon.

###############################################################################

# 2. Directives customise processing or output in some way.
# You don't need any directives to get started.
# But they can add more error checking, or change how things are displayed.
# They begin with a word, letter, or symbol. 
# They are most often placed at the top, before transactions.

account assets             ; Declare valid account names and display order.
account assets:savings     ; A subaccount. This one represents a bank account.
account assets:checking    ; Another. Note, 2+ spaces after the account name.
account assets:receivable  ; Accounting type is inferred from english names,
account passifs            ; or declared with a "type" tag, type:L
account expenses           ; type:X
                           ; A follow-on comment line, indented.
account expenses:rent      ; Expense and revenue categories are also accounts.
                           ; Subaccounts inherit their parent's type.

commodity $0.00         ; Declare valid commodities and their display styles.
commodity 1.000,00 EUR

decimal-mark .          ; The decimal mark used in this file (if ambiguous).

payee Whole Foods       ; Declare a valid payee name.

tag trip                ; Declare a valid tag name.

P 2024-03-01 AAPL $179  ; Declare a market price for AAPL in $ on this date.

include other.journal   ; Include another journal file here.

# Declare a recurring "periodic transaction", for budget/forecast reports
~ monthly  set budget goals  ; <- Note, 2+ spaces before the description.
    (expenses:rent)      $1000
    (expenses:food)       $500

# Declare an auto posting rule, to modify existing transactions in reports
= revenues:consulting
    liabilities:tax:2024:us          *0.25  ; Add a tax liability & expense
    expenses:tax:2024:us            *-0.25  ; for 25% of the revenue.

###############################################################################

# 3. Transactions are what it's all about.
# They are dated events, usually movements of money between 2 or more accounts.
# They begin with a numeric date.
# Here is their basic shape:
#
# DATE DESCRIPTION    ; The transaction's date and optional description.
#   ACCOUNT1  AMOUNT  ; A posting of an amount to/from this account, indented.
#   ACCOUNT2  AMOUNT  ; A second posting, balancing the first.
#   ...               ; More if needed. Amounts must sum to zero.
#                     ; Note, 2+ spaces between account names and amounts.

2024-01-01 opening balances         ; At the start, declare pre-existing balances this way.
    assets:savings          $10000  ; Account names can be anything. lower case is easy to type.
    assets:checking          $1000  ; assets, liabilities, equity, revenues, expenses are common.
    liabilities:credit card  $-500  ; liabilities, equity, revenues balances are usually negative.
    equity:start                    ; One amount can be left blank. $-10500 is inferred here.
                                    ; Some of these accounts we didn't declare above,
                                    ; so -s/--strict would complain.

2024-01-03 ! (12345) pay rent
    ; Additional transaction comment lines, indented.
    ; There can be a ! or * after the date meaning "pending" or "cleared".
    ; There can be a parenthesised (code) after the date/status.
                                    ; Amounts' sign shows direction of flow.
    assets:checking          $-500  ; Minus means removed from this account (credit).
    expenses:rent             $500  ; Plus means added to this account (debit).

; Keeping transactions in date order is optional (but helps error checking).

2024-01-02 Gringott's Bank | withdrawal  ; Description can be PAYEE | NOTE
    assets:bank:gold       -10 gold
    assets:pouch            10 gold

2024-01-02 shopping
    expenses:clothing        1 gold
    expenses:wands           5 gold
    assets:pouch            -6 gold

2024-01-02 receive gift
    revenues:gifts          -3 "Chocolate Frogs"  ; Complex commodity symbols
    assets:pouch             3 "Chocolate Frogs"  ; must be in double quotes.

2024-01-15 buy some shares, in two lots                 ; Cost can be noted.
    assets:investments:2024-01-15     2.0 AAAA @ $1.50  ; @  means per-unit cost
    assets:investments:2024-01-15-02  3.0 AAAA @@ $4    ; @@ means total cost
                      ; ^ Per-lot subaccounts are sometimes useful.
    assets:checking                 $-7

2024-01-15 assert some account balances on this date
    ; Balances can be asserted in any transaction, with =, for extra error checking.
    ; Assertion txns like this one can be made with hledger close --assert --show-costs
    ;
    assets:savings                    $0                   = $10000
    assets:checking                   $0                   =   $493
    assets:bank:gold                   0 gold              =    -10 gold
    assets:pouch                       0 gold              =      4 gold
    assets:pouch                       0 "Chocolate Frogs" =      3 "Chocolate Frogs"
    assets:investments:2024-01-15      0.0 AAAA            =      2.0 AAAA @  $1.50
    assets:investments:2024-01-15-02   0.0 AAAA            =      3.0 AAAA @@ $4
    liabilities:credit card           $0                   =  $-500

2024-02-01 note some event, or a transaction not yet fully entered, on this date
    ; Postings are not required.

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

Comments

Lines in the journal will be ignored if they begin with a hash (#) or a semicolon (;). (See also Other syntax.) hledger will also ignore regions beginning with a comment line and ending with an end comment line (or file end). Here's a suggestion for choosing between them:

  • # for top-level notes
  • ; for commenting out things temporarily
  • comment for quickly commenting large regions (remember it's there, or you might get confused)

Eg:

# a comment line
; another commentline
comment
A multi-line comment block,
continuing until "end comment" directive
or the end of the current file.
end comment

Some hledger entries can have same-line comments attached to them, from ; (semicolon) to end of line. See Transaction comments, Posting comments, and Account comments below.

Transactions

Transactions are the main unit of information in a journal file. They represent events, typically a movement of some quantity of commodities between two or more named accounts.

Each transaction is recorded as a journal entry, beginning with a simple date in column 0. This can be followed by any of the following optional fields, separated by spaces:

  • a status character (empty, !, or *)
  • a code (any short number or text, enclosed in parentheses)
  • a description (any remaining text until end of line or a semicolon)
  • a comment (any remaining text following a semicolon until end of line, and any following indented lines beginning with a semicolon)
  • 0 or more indented posting lines, describing what was transferred and the accounts involved (indented comment lines are also allowed, but not blank lines or non-indented lines).

Here's a simple journal file containing one transaction:

2008/01/01 income
  assets:bank:checking   $1
  income:salary         $-1

Dates

Simple dates

Dates in the journal file use simple dates format: YYYY-MM-DD or YYYY/MM/DD or YYYY.MM.DD, with leading zeros optional. The year may be omitted, in which case it will be inferred from the context: the current transaction, the default year set with a Y directive, or the current date when the command is run. Some examples: 2010-01-31, 2010/01/31, 2010.1.31, 1/31.

(The UI also accepts simple dates, as well as the more flexible smart dates documented in the hledger manual.)

Posting dates

You can give individual postings a different date from their parent transaction, by adding a posting comment containing a tag (see below) like date:DATE. This is probably the best way to control posting dates precisely. Eg in this example the expense should appear in May reports, and the deduction from checking should be reported on 6/1 for easy bank reconciliation:

2015/5/30
    expenses:food     $10  ; food purchased on saturday 5/30
    assets:checking        ; bank cleared it on monday, date:6/1
$ hledger -f t.j register food
2015-05-30                      expenses:food                  $10           $10
$ hledger -f t.j register checking
2015-06-01                      assets:checking               $-10          $-10

DATE should be a simple date; if the year is not specified it will use the year of the transaction's date.
The date: tag must have a valid simple date value if it is present, eg a date: tag with no value is not allowed.

Status

Transactions (or individual postings within a transaction) can have a status mark, which is a single character before the transaction description (or posting account name), separated from it by a space, indicating one of three statuses:

mark  status
 unmarked
!pending
*cleared

When reporting, you can filter by status with the -U/--unmarked, -P/--pending, and -C/--cleared flags (and you can combine these, eg -UP to match all except cleared things). Or you can use the status:, status:!, and status:* queries, or the U, P, C keys in hledger-ui.

(Note: in Ledger the "unmarked" state is called "uncleared"; in hledger we renamed it to "unmarked" for semantic clarity.)

Status marks are optional, but can be helpful eg for reconciling with real-world accounts. Some editor modes provide highlighting and shortcuts for working with status. Eg in Emacs ledger-mode, you can toggle transaction status with C-c C-e, or posting status with C-c C-c.

What "uncleared", "pending", and "cleared" actually mean is up to you. Here's one suggestion:

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

Code

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.

Description

After the date, status mark and/or code fields, the rest of the line (or until a comment is begun with ;) is the transaction's description. Here you can describe the transaction (called the "narration" in traditional bookkeeping), or you can record a payee/payer name, or you can leave it empty.

Transaction descriptions show up in print output and in register reports, and can be listed with the descriptions command.

You can query by description with desc:DESCREGEX, or pivot on description with --pivot desc.

Payee and note

Sometimes people want a dedicated payee/payer field that can be queried and checked more strictly. If you want that, you can write a | (pipe) character in the description. This divides it into a "payee" field on the left, and a "note" field on the right. (Either can be empty.)

You can query these with payee:PAYEEREGEX and note:NOTEREGEX, list their values with the payees and notes commands, or pivot on payee or note.

Note: in transactions with no | character, description, payee, and note all have the same value. Once a | is added, they become distinct. (If you'd like to change this behaviour, please propose it on the mail list.)

If you want more strict error checking, you can declare the valid payee names with payee directives, and then enforce these with hledger check payees. (Note: because of the above, for this you'll need to ensure every transaction description contains a | and therefore a checkable payee name, even if it's empty.)

Transaction comments

Text following ;, after a transaction description, and/or on indented lines immediately below it, form comments for that transaction. They are reproduced by print but otherwise ignored, except they may contain tags, which are not ignored.

2012-01-01 something  ; a transaction comment
    ; a second line of transaction comment
    expenses   1
    assets

Postings

A posting is an addition of some amount to, or removal of some amount from, an account. Each posting line begins with at least one space or tab (2 or 4 spaces is common), followed by:

  • (optional) a status character (empty, !, or *), followed by a space
  • (required) an account name (any text, optionally 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:

assets
assets:bank
assets:bank:checking
expenses
expenses:food

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

assets
 bank
  checking
expenses
 food

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.

Amounts

After the account name, there is usually an amount. (Remember: between account name and amount, there must be two or more spaces.)

hledger's amount format is flexible, supporting several international formats. Here are some examples. Amounts have a number (the "quantity"):

1

..and usually a currency symbol or commodity name (more on this below), to the left or right of the quantity, with or without a separating space:

$1
4000 AAPL
3 "green apples"

Amounts can be preceded by a minus sign (or a plus sign, though plus is the default), The sign can be written before or after a left-side commodity symbol:

-$1
$-1

One or more spaces between the sign and the number are acceptable when parsing (but they won't be displayed in output):

+ $1
$-      1

Scientific E notation is allowed:

1E-6
EUR 1E3

Decimal marks

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

1.23
1,23

Both of these are common in international number formats, so hledger is not biased towards one or the other. Because hledger also supports digit group marks (eg thousands separators), this means that a number like 1,000 or 1.000 containing just one period or comma is ambiguous. In such cases, hledger by default assumes it is a decimal mark, and will parse both of those as 1.

To help hledger parse such ambiguous numbers more accurately, if you use digit group marks, we recommend declaring the decimal mark explicitly. The best way is to add a decimal-mark directive at the top of each data file, like this:

decimal-mark .

Or you can declare it per commodity with commodity directives, described below.

hledger also accepts numbers like 10. with no digits after the decimal mark (and will sometimes display numbers that way to disambiguate them - see Trailing decimal marks).

Digit group marks

In the integer part of the amount quantity (left of the decimal mark), groups of digits can optionally be separated by a digit group mark - a comma or period (whichever is not used as decimal mark), or a space (several Unicode space variants, like no-break space, are also accepted). So these are all valid amounts in a journal file:

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

Commodity

Amounts in hledger have both a "quantity", which is a signed decimal number, and a "commodity", which is a currency symbol, stock ticker, or any word or phrase describing something you are tracking.

If the commodity name contains non-letters (spaces, numbers, or punctuation), you must always write it inside double quotes ("green apples", "ABC123").

If you write just a bare number, that too will have a commodity, with name ""; we call that the "no-symbol commodity".

Actually, hledger combines these single-commodity amounts into more powerful multi-commodity amounts, which are what it works with most of the time. A multi-commodity amount could be, eg: 1 USD, 2 EUR, 3.456 TSLA. In practice, you will only see multi-commodity amounts in hledger's output; you can't write them directly in the journal file.

By default, the format of amounts in the journal influences how hledger displays them in output. This is explained in Commodity display style below.

Costs

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:

    2009/1/1
      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:

    2009/1/1
      assets:euros     €100 @@ $135  ; one hundred euros purchased at $135 for the lot
      assets:dollars
    
  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:

    2009/1/1
      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:

2013/1/1
  a   $1 =  $1
  b      = $-1

2013/1/2
  a   $1 =  $2
  b  $-1 = $-2

After reading a journal file, hledger will check all balance assertions and report an error if any of them fail. Balance assertions can protect you from, eg, inadvertently disrupting reconciled balances while cleaning up old entries. You can disable them temporarily with the -I/--ignore-assertions flag, which can be useful for troubleshooting or for reading Ledger files. (Note: this flag currently does not disable balance assignments, described below).

Assertions and ordering

hledger calculates and checks an account's balance assertions in date order (and when there are multiple assertions on the same day, in parse order). Note this is different from Ledger, which checks assertions always in parse order, ignoring dates.

This means in hledger you can freely reorder transactions, postings, or files, and balance assertions will usually keep working. The exception is when you reorder multiple postings on the same day, to the same account, which have balance assertions; those will likely need updating.

Assertions and multiple 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:

2019/1/1
  (a)     $1 @ €1 = $1

We do allow costs to be written in balance assertion amounts, however, and print shows them, but they don't affect whether the assertion passes or fails. This is for backward compatibility (hledger's close command used to generate balance assertions with costs), and because balance assignments do use costs (see below).

Assertions and commodities

The balance assertions described so far are "single commodity balance assertions": they assert and check the balance in one commodity, ignoring any others that may be present. This is how balance assertions work in Ledger also.

If an account contains multiple commodities, you can assert their balances by writing multiple postings with balance assertions, one for each commodity:

2013/1/1
  usd   $-1
  eur   €-1
  both

2013/1/2
  both    0 = $1
  both    0 = €1

In hledger you can make a stronger "sole commodity balance assertion" by writing two equals signs (== EXPECTEDBALANCE). This also asserts that there are no other commodities in the account besides the asserted one (or at least, that their current balance is zero):

2013/1/1
  usd   $-1  == $-1  ; these sole commodity assertions succeed
  eur   €-1  == €-1
  both      ;==  $1  ; this one would fail because 'both' contains $ and €

It's less easy to make a "sole commodities balance assertion" (note the plural) - ie, asserting that an account contains two or more specified commodities and no others. It can be done by

  1. isolating each commodity in a subaccount, and asserting those
  2. and also asserting there are no commodities in the parent account itself:
2013/1/1
  usd       $-1
  eur       €-1
  both        0 == 0   ; nothing up my sleeve
  both:usd   $1 == $1  ; a dollar here
  both:eur   €1 == €1  ; a euro there

Assertions and subaccounts

All of the balance assertions above (both = and ==) are "subaccount-exclusive balance assertions"; they ignore any balances that exist in deeper subaccounts.

In hledger you can make "subaccount-inclusive balance assertions" by adding a star after the equals (=* or ==*):

2019/1/1
  equity:start
  assets:checking  $10
  assets:savings   $10
  assets            $0 ==* $20  ; assets + subaccounts contains $20 and nothing else

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

2012-01-01
    expenses   1  ; a comment for posting 1
    assets
    ; a comment for posting 2
    ; a second comment line for posting 2

Transaction balancing

How exactly does hledger decide when a transaction is balanced ? 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

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

Directives

Besides transactions, there is something else you can put in a journal file: directives. These are declarations, beginning with a keyword, that modify hledger's behaviour. Some directives can have more specific subdirectives, indented below them. hledger's directives are similar to Ledger's in many cases, but there are also many differences. Directives are not required, but can be useful. Here are the main directives:

purposedirective
READING DATA:
Rewrite account namesalias
Comment out sections of the filecomment
Declare file's decimal mark, to help parse amounts accuratelydecimal-mark
Include other data filesinclude
GENERATING DATA:
Generate recurring transactions or budget goals~
Generate extra postings on existing transactions=
CHECKING FOR ERRORS:
Define valid entities to provide more error checkingaccount, commodity, payee, tag
REPORTING:
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.
N
aliasRewrites account names, in following entries until end of current file or end aliases.
Command line equivalent: --alias
Y
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
N,
N,
Y,
Y
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
N
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.
Y,
Y,
N,
N
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
assets
liabilities
equity
revenues
expenses

If there are undeclared accounts, those will be displayed last, in alphabetical order.

Sorting is done within each group of sibling accounts, at each level of the account tree. Eg, a declaration like account parent:child influences child's position among its siblings.

Note, it does not affect parent's position; for that, you need an account parent declaration.

Sibling accounts are always displayed together; hledger won't display x:y in between a:b and a:c.

An account directive both declares an account as a valid posting target, and declares its display order; you can't easily do one without the other.

Account types

hledger knows that 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.)

Eg:

alias /REGEX/ = REPLACEMENT

or:

$ hledger --alias '/REGEX/=REPLACEMENT' ...

Any part of an account name matched by REGEX will be replaced by REPLACEMENT. REGEX is case-insensitive as usual.

If you need to match a forward slash, escape it with a backslash, eg /\/=:.

If REGEX contains parenthesised match groups, these can be referenced by the usual backslash and number in REPLACEMENT:

alias /^(.+):bank:([^:]+):(.*)/ = \1:\2 \3
; rewrites "assets:bank:wells fargo:checking" to  "assets:wells fargo checking"

REPLACEMENT continues to the end of line (or on command line, to end of option argument), so it can contain trailing whitespace.

Combining aliases

You can define as many aliases as you like, using journal directives and/or command line options.

Recursive aliases - where an account name is rewritten by one alias, then by another alias, and so on - are allowed. Each alias sees the effect of previously applied aliases.

In such cases it can be important to understand which aliases will be applied and in which order. For (each account name in) each journal entry, we apply:

  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
  bar

This means that account aliases should usually be declared at the start of your top-most file, like this:

alias foo=Foo
alias bar=Bar

2023-01-01  ; affected by aliases above
  foo  1
  bar

include c.journal  ; also affected

end aliases directive

You can clear (forget) all currently defined aliases (seen in the journal so far, or defined on the command line) with this directive:

end aliases

Aliases can generate bad account names

Be aware that account aliases can produce malformed account names, which could cause confusing reports or invalid print output. For example, you could erase all account names:

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

The above print output is not a valid journal. Or you could insert an illegal double space, causing print output that would give a different journal when reparsed:

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

Aliases and account types

If an account with a type declaration (see Declaring accounts > Account types) is renamed by an alias, normally the account type remains in effect.

However, renaming in a way that reshapes the account tree (eg renaming parent accounts but not their children, or vice versa) could prevent child accounts from inheriting the account type of their parents.

Secondly, if an account's type is being inferred from its name, renaming it by an alias could prevent or alter that.

If you are using account aliases and the type: query is not matching accounts as you expect, try troubleshooting with the accounts command, eg something like:

$ hledger accounts --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 .

or

decimal-mark ,

This prevents any ambiguity when parsing numbers in the file, so we recommend it, especially if the file contains digit group marks (eg thousands separators).

include directive

You can pull in the content of additional files by writing an include directive, like this:

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

P DATE COMMODITY1SYMBOL COMMODITY2AMOUNT

DATE is a simple date, COMMODITY1SYMBOL is the symbol of the commodity being priced, and COMMODITY2AMOUNT is the amount (symbol and quantity) of commodity 2 that one unit of commodity 1 is worth on this date. Examples:

# one euro was worth $1.35 from 2009-01-01 onward:
P 2009-01-01 € $1.35

# and $1.40 from 2010-01-01 onward:
P 2010-01-01 € $1.40

The -V, -X and --value flags use these market prices to show amount values in another commodity. See Value reporting.

payee directive

payee PAYEE NAME

This directive can be used to declare a limited set of payees which may appear in transaction descriptions. The "payees" check will report an error if any transaction refers to a payee that has not been declared. Eg:

payee Whole Foods    ; a comment

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

To declare the empty payee name, use "".

payee ""

Ledger-style indented subdirectives, if any, are currently ignored.

tag directive

tag TAGNAME

This directive can be used to declare a limited set of tag names allowed in tags. TAGNAME should be a valid tag name (no spaces). Eg:

tag  item-id

Any indented subdirectives are currently ignored.

The "tags" check will report an error if any undeclared tag name is used. It is quite easy to accidentally create a tag through normal use of colons in comments; if you want to prevent this, you can declare and check your tags .

Periodic transactions

The ~ directive declares a "periodic rule" which generates temporary extra transactions, usually recurring at some interval, when hledger is run with the --forecast flag. These "forecast transactions" are useful for forecasting future activity. They exist only for the duration of the report, and only when --forecast is used; they are not saved in the journal file by hledger.

Periodic rules also have a second use: with the --budget flag they set budget goals for budgeting.

Periodic rules can be a little tricky, so before you use them, read this whole section, or at least the following tips:

  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
    assets:bank:checking

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

The period expression is the same syntax used for specifying multi-period reports, just interpreted differently; there, it specifies report periods; here it specifies recurrence dates (the periods' start dates).

Periodic rules and relative dates

Partial or relative dates (like 12/31, 25, tomorrow, last week, next quarter) are usually not recommended in periodic rules, since the results will change as time passes. If used, they will be interpreted relative to, in order of preference:

  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

So,

  • Do write two spaces between your period expression and your transaction description, if any.
  • Don't accidentally write two spaces in the middle of your period expression.

Auto postings

The = directive declares an "auto posting rule", which adds extra postings to existing transactions. (Remember, postings are the account name & amount lines below a transaction's date & description.)

In the journal, an auto posting rule looks quite like a transaction, but instead of date and description it has = (mnemonic: "match") and a query, like this:

= QUERY
    ACCOUNT    AMOUNT
    ...

Queries are just like command line queries; an account name substring is most common. Query terms containing spaces should be enclosed in single or double quotes.

Each = rule works like this: when hledger is run with the --auto flag, wherever the QUERY matches a posting in the journal, the rule's postings are added to that transaction, immediately below the matched posting. Note these generated postings are temporary, existing only for the duration of the report, and only when --auto is used; they are not saved in the journal file by hledger.

Generated postings' amounts can depend on the matched posting's amount. So auto postings can be useful for, eg, adding tax postings with a standard percentage. AMOUNT can be:

  • a number with no commodity symbol, like 2. The matched posting's commodity symbol will be added to this.

  • a normal amount with a commodity symbol, like $2. This will be used as-is.

  • an asterisk followed by a number, like *2. This will multiply the matched posting's amount (and total price, if any) by the number.

  • an asterisk followed by an amount with commodity symbol, like *$2. This multiplies and also replaces the commodity symbol with this new one.

Some examples:

; every time I buy food, schedule a dollar donation
= expenses:food
    (liabilities:charity)   $-1

; when I buy a gift, also deduct that amount from a budget envelope subaccount
= expenses:gifts
    assets:checking:gifts  *-1
    assets:checking         *1

2017/12/1
  expenses:food    $10
  assets:checking

2017/12/14
  expenses:gifts   $20
  assets:checking
$ hledger print --auto
2017-12-01
    expenses:food              $10
    assets:checking
    (liabilities:charity)      $-1

2017-12-14
    expenses:gifts             $20
    assets:checking
    assets:checking:gifts     -$20
    assets:checking            $20

Note that depending fully on generated data such as this has some drawbacks - it's less portable, less future-proof, less auditable by others, and less robust (eg your balance assertions will depend on whether you use or don't use --auto). An alternative is to use auto postings in "one time" fashion - use them to help build a complex journal entry, view it with hledger print --auto, and then copy that output into the journal file to make it permanent.

Auto postings and multiple files

An auto posting rule can affect any transaction in the current file, or in any parent file or child file. Note, currently it will not affect sibling files (when multiple -f/--file are used - see #1212).

Auto postings and dates

A posting date (or secondary date) in the matched posting, or (taking precedence) a posting date in the auto posting rule itself, will also be used in the generated posting.

Auto postings and transaction balancing / inferred amounts / balance assertions

Currently, auto postings are added:

Note this means that journal entries must be balanced both before and after auto postings are added. This changed in hledger 1.12+; see #893 for background.

This also means that you cannot have more than one auto-posting with a missing amount applied to a given transaction, as it will be unable to infer amounts.

Auto posting tags

Automated postings will have some extra tags:

  • generated-posting:= QUERY - shows this was generated by an auto posting rule, and the query
  • _generated-posting:= QUERY - a hidden tag, which does not appear in hledger's output. This can be used to match postings generated "just now", rather than generated in the past and saved to the journal.

Also, any transaction that has been changed by auto posting rules will have these tags added:

  • modified: - this transaction was modified
  • _modified: - a hidden tag not appearing in the comment; this transaction was modified "just now".
Auto postings on forecast transactions only

Tip: you can can make auto postings that will apply to forecast transactions but not recorded transactions, by adding tag:_generated-transaction to their QUERY. This can be useful when generating new journal entries to be saved in the journal.

Other syntax

hledger journal format supports quite a few other features, mainly to make interoperating with or converting from Ledger easier. Note some of the features below are powerful and can be useful in special cases, but in general, features in this section are considered less important or even not recommended for most users. Downsides are mentioned to help you decide if you want to use them.

Balance assignments

Ledger-style balance assignments are also supported. These are like balance assertions, but with no posting amount on the left side of the equals sign; instead it is calculated automatically so as to satisfy the assertion. This can be a convenience during data entry, eg when setting opening balances:

; starting a new journal, set asset account balances
2016/1/1 opening balances
  assets:checking            = $409.32
  assets:savings             = $735.24
  assets:cash                 = $42
  equity:opening balances

or when adjusting a balance to reality:

; no cash left; update balance, record any untracked spending as a generic expense
2016/1/15
  assets:cash    = $0
  expenses:misc

The calculated amount depends on the account's balance in the commodity at that point (which depends on the previously-dated postings of the commodity to that account since the last balance assertion or assignment).

Downsides: using balance assignments makes your journal less explicit; to know the exact amount posted, you have to run hledger or do the calculations yourself, instead of just reading it. Also balance assignments' forcing of balances can hide errors. These things make your financial data less portable, less future-proof, and less trustworthy in an audit.

Balance assignments and costs

A cost in a balance assignment will cause the calculated amount to have that cost attached:

2019/1/1
  (a)             = $1 @ €2
$ hledger print --explicit
2019-01-01
    (a)         $1 @ €2 = $1 @ €2
Balance assignments and multiple files

Balance assignments handle multiple files like balance assertions. They see balance from other files previously included from the current file, but not from previous sibling or parent files.

Bracketed posting dates

For setting posting dates and secondary posting dates, Ledger's bracketed date syntax is also supported: [DATE], [DATE=DATE2] or [=DATE2] in posting comments. hledger will attempt to parse any square-bracketed sequence of the 0123456789/-.= characters in this way. With this syntax, DATE infers its year from the transaction and DATE2 infers its year from DATE.

Downsides: another syntax to learn, redundant with hledger's date:/date2: tags, and confusingly similar to Ledger's lot date syntax.

D directive

D AMOUNT

This directive sets a default commodity, to be used for any subsequent commodityless amounts (ie, plain numbers) seen while parsing the journal. This effect lasts until the next D directive, or the end of the current file.

For compatibility/historical reasons, D also acts like a commodity directive (setting the commodity's decimal mark for parsing and display style for output). So its argument is not just a commodity symbol, but a full amount demonstrating the style. The amount must include a decimal mark (either period or comma). Eg:

; commodity-less amounts should be treated as dollars
; (and displayed with the dollar sign on the left, thousands separators and two decimal places)
D $1,000.00

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

Interactions with other directives:

For setting a commodity's display style, a commodity directive has highest priority, then a D directive.

For detecting a commodity's decimal mark during parsing, decimal-mark has highest priority, then commodity, then D.

For checking commodity symbols with the check command, a commodity directive is required (hledger check commodities ignores D directives).

Downsides: omitting commodity symbols makes your financial data less explicit, less portable, and less trustworthy in an audit. It is usually an unsustainable shortcut; sooner or later you will want to track multiple commodities. D is overloaded with functions redundant with commodity and decimal-mark. And it works differently from Ledger's D.

apply account directive

This directive sets a default parent account, which will be prepended to all accounts in following entries, until an end apply account directive or end of current file. Eg:

apply account home

2010/1/1
    food    $10
    cash

end apply account

is equivalent to:

2010/01/01
    home:food           $10
    home:cash          $-10

account directives are also affected, and so is any included content.

Account names entered via hledger add or hledger-web are not affected.

Account aliases, if any, are applied after the parent account is prepended.

Downsides: this can make your financial data less explicit, less portable, and less trustworthy in an audit.

Y directive

Y YEAR

or (deprecated backward-compatible forms):

year YEAR apply year YEAR

The space is optional. This sets a default year to be used for subsequent dates which don't specify a year. Eg:

Y2009  ; set default year to 2009

12/15  ; equivalent to 2009/12/15
  expenses  1
  assets

year 2010  ; change default year to 2010

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

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

Downsides: omitting the year (from primary transaction dates, at least) makes your financial data less explicit, less portable, and less trustworthy in an audit. Such dates can get separated from their corresponding Y directive, eg when evaluating a region of the journal in your editor. A missing Y directive makes reports dependent on today's date.

Secondary dates

A secondary date is written after the primary date, following an equals sign. 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
python
  PYTHONCODE
tag         NAME
value       EXPR
--command-line-flags

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:

  • @ UNITCOST and @@ TOTALCOST
    • 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:

  • @ UNITCOST and @@ TOTALCOST
    • 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)
  • {UNITCOST} and {{TOTALCOST}}
    • 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.

CSV

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.

source

If you tell hledger to read a csv file with -f foo.csv, it will look for rules in foo.csv.rules. Or, you can tell it to read the rules file, with -f foo.csv.rules, and it will look for data in foo.csv (since 1.30).

These are mostly equivalent, but the second method provides some extra features. For one, the data file can be missing, without causing an error; it is just considered empty. 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".

separator

You can use the separator rule to read other kinds of character-separated data. The argument is any single separator character, or the words tab or space (case insensitive). Eg, for comma-separated values (CSV):

separator ,

or for semicolon-separated values (SSV):

separator ;

or for tab-separated values (TSV):

separator TAB

If the input file has a .csv, .ssv or .tsv file extension (or a csv:, ssv:, tsv: prefix), the appropriate separator will be inferred automatically, and you won't need this rule.

skip

skip N

The word skip followed by a number (or no number, meaning 1) tells hledger to ignore this many non-empty lines at the start of the input data. You'll need this whenever your CSV data contains header lines. Note, empty and blank lines are skipped automatically, so you don't need to count those.

skip has a second meaning: it can be used inside if blocks (described below), to skip one or more records whenever the condition is true. Records skipped in this way are ignored, except they are still required to be valid CSV.

date-format

date-format DATEFMT

This is a helper for the date (and date2) fields. If your CSV dates are not formatted like YYYY-MM-DD, YYYY/MM/DD or YYYY.MM.DD, you'll need to add a date-format rule describing them with a strptime-style date parsing pattern - see https://hackage.haskell.org/package/time/docs/Data-Time-Format.html#v:formatTime. The pattern must parse the CSV date value completely. Some examples:

# MM/DD/YY
date-format %m/%d/%y
# D/M/YYYY
# The - makes leading zeros optional.
date-format %-d/%-m/%Y
# YYYY-Mmm-DD
date-format %Y-%h-%d
# M/D/YYYY HH:MM AM some other junk
# Note the time and junk must be fully parsed, though only the date is used.
date-format %-m/%-d/%Y %l:%M %p some other junk

timezone

timezone TIMEZONE

When CSV contains date-times that are implicitly in some time zone other than yours, but containing no explicit time zone information, you can use this rule to declare the CSV's native time zone, which helps prevent off-by-one dates.

When the CSV date-times do contain time zone information, you don't need this rule; instead, use %Z in date-format (or %z, %EZ, %Ez; see the formatTime link above).

In either of these cases, hledger will do a time-zone-aware conversion, localising the CSV date-times to your current system time zone. If you prefer to localise to some other time zone, eg for reproducibility, you can (on unix at least) set the output timezone with the TZ environment variable, eg:

$ TZ=-1000 hledger print -f foo.csv  # or TZ=-1000 hledger import foo.csv

timezone currently does not understand timezone names, except "UTC", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", or "PDT". For others, use numeric format: +HHMM or -HHMM.

newest-first

hledger tries to ensure that the generated transactions will be ordered chronologically, including same-day transactions. Usually it can auto-detect how the CSV records are ordered. But if it encounters CSV where all records are on the same date, it assumes that the records are oldest first. If in fact the CSV's records are normally newest first, like:

2022-10-01, txn 3...
2022-10-01, txn 2...
2022-10-01, txn 1...

you can add the newest-first rule to help hledger generate the transactions in correct order.

# same-day CSV records are newest first
newest-first

intra-day-reversed

If CSV records within a single day are ordered opposite to the overall record order, you can add the intra-day-reversed rule to improve the order of journal entries. Eg, here the overall record order is newest first, but same-day records are oldest first:

2022-10-02, txn 3...
2022-10-02, txn 4...
2022-10-01, txn 1...
2022-10-01, txn 2...
# transactions within each day are reversed with respect to the overall date order
intra-day-reversed

decimal-mark

decimal-mark .

or:

decimal-mark ,

hledger automatically accepts either period or comma as a decimal mark when parsing numbers (cf Amounts). However if any numbers in the CSV contain digit group marks, such as thousand-separating commas, you should declare the decimal mark explicitly with this rule, to avoid misparsed numbers.

fields list

fields FIELDNAME1, FIELDNAME2, ...

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

  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

HLEDGERFIELD FIELDVALUE

Field assignments are the more flexible way to assign CSV values to hledger fields. They can be used instead of or in addition to a fields list (see above).

To assign a value to a hledger field, write the field name (any of the standard hledger field/pseudo-field names, defined below), a space, followed by a text value on the same line. This text value may interpolate CSV fields, referenced either by their 1-based position in the CSV record (%N) or by the name they were given in the fields list (%CSVFIELD), and regular expression match groups (\N).

Some examples:

# set the amount to the 4th CSV field, with " USD" appended
amount %4 USD

# combine three fields to make a comment, containing note: and date: tags
comment note: %somefield - %anotherfield, date: %1

Tips:

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

or

if
MATCHER
MATCHER
MATCHER
 RULE
 RULE

If any of the matchers succeeds, all of the indented rules will be applied. They are usually field assignments, but the following special rules may also be used within an if block:

  • skip - skips the matched CSV record (generating no transaction from it)
  • end - skips the rest of the current CSV file.

Some examples:

# if the record contains "groceries", set account2 to "expenses:groceries"
if groceries
 account2 expenses:groceries
# if the record contains any of these phrases, set account2 and a transaction comment as shown
if
monthly service fee
atm transaction fee
banking thru software
 account2 expenses:business:banking
 comment  XXX deductible ? check it
# if an empty record is seen (assuming five fields), ignore the rest of the CSV file
if ,,,,
 end

Matchers

There are two kinds:

  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:

if,HLEDGERFIELD1,HLEDGERFIELD2,...
MATCHERA,VALUE1,VALUE2,...
MATCHERB,VALUE1,VALUE2,...
; Comment line that explains MATCHERC
MATCHERC,VALUE1,VALUE2,...
<empty line>

The first character after if is taken to be this if table's field separator. It is unrelated to the separator used in the CSV file. It should be a non-alphanumeric character like , or | that does not appear anywhere else in the table (it should not be used in field names or matchers or values, and it cannot be escaped with a backslash).

Each line must contain the same number of separators; empty values are allowed. Whitespace can be used in the matcher lines for readability (but not in the if line, currently). You can use the comment lines in the table body. The table must be terminated by an empty line (or end of file).

An if table like the above is interpreted as follows: try all of the 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:

if MATCHERA
  HLEDGERFIELD1 VALUE1
  HLEDGERFIELD2 VALUE2
  ...

if MATCHERB
  HLEDGERFIELD1 VALUE1
  HLEDGERFIELD2 VALUE2
  ...

; Comment line which explains MATCHERC
if MATCHERC
  HLEDGERFIELD1 VALUE1
  HLEDGERFIELD2 VALUE2
  ...

Example:

if,account2,comment
atm transaction fee,expenses:business:banking,deductible? check it
%description groceries,expenses:groceries,
;; Comment line that desribes why this particular date is special
2023/01/12.*Plumbing LLC,expenses:house:upkeep,emergency plumbing call-out

balance-type

Balance assertions generated by assigning to balanceN are of the simple = type by default, which is a single-commodity, subaccount-excluding assertion. You may find the subaccount-including variants more useful, eg if you have created some virtual subaccounts of checking to help with budgeting. You can select a different type of assertion with the balance-type rule:

# balance assertions will consider all commodities and all subaccounts
balance-type ==*

Here are the balance assertion types for quick reference:

=    single commodity, exclude subaccounts
=*   single commodity, include subaccounts
==   multi commodity,  exclude subaccounts
==*  multi commodity,  include subaccounts

include

include RULESFILE

This includes the contents of another CSV rules file at this point. RULESFILE is an absolute file path or a path relative to the current file's directory. This can be useful for sharing common rules between several rules files, eg:

# someaccount.csv.rules

## someaccount-specific rules
fields   date,description,amount
account1 assets:someaccount
account2 expenses:misc

## common rules
include categorisation.rules

Working with CSV

Some tips:

Rapid feedback

It's a good idea to get rapid feedback while creating/troubleshooting CSV rules. Here's a good way, using entr from eradman.com/entrproject:

$ ls foo.csv* | entr bash -c 'echo ----; hledger -f foo.csv print desc:SOMEDESC'

A desc: query (eg) is used to select just one, or a few, transactions of interest. "bash -c" is used to run multiple commands, so we can echo a separator each time the command re-runs, making it easier to read the output.

Valid CSV

Note that hledger will only accept valid CSV conforming to RFC 4180, and equivalent SSV and TSV formats (like RFC 4180 but with semicolon or tab as separators). This means, eg:

  • Values may be enclosed in double quotes, or not. Enclosing in single quotes is not allowed. (Eg 'A','B' is rejected.)
  • When values are enclosed in double quotes, spaces outside the quotes are not allowed. (Eg "A", "B" is rejected.)
  • When values are not enclosed in quotes, they may not contain double quotes. (Eg A"A, 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):

2023-01-01,foo,$123.00

you don't have to do anything special for the commodity symbol, it will be assigned as part of the amount. Eg:

fields date,description,amount
2023-01-01 foo
    expenses:unknown         $123.00
    income:unknown          $-123.00

If the currency is provided as a separate CSV field:

2023-01-01,foo,USD,123.00

You can assign that to the currency pseudo-field, which has the special effect of prepending itself to every amount in the transaction (on the left, with no separating space):

fields date,description,currency,amount
2023-01-01 foo
    expenses:unknown       USD123.00
    income:unknown        USD-123.00

Or, you can use a field assignment to construct the amount yourself, with more control. Eg to put the symbol on the right, and separated by a space:

fields date,description,cur,amt
amount %amt %cur
2023-01-01 foo
    expenses:unknown        123.00 USD
    income:unknown         -123.00 USD

Note we used a temporary field name (cur) that is not currency - that would trigger the prepending effect, which we don't want here.

Amount decimal places

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:

Date,Details,Debit,Credit,Balance
07/12/2012,LODGMENT       529898,,10.0,131.21
07/12/2012,PAYMENT,5,,126
# bankofireland-checking.csv.rules

# skip the header line
skip

# name the csv fields, and assign some of them as journal entry fields
fields  date, description, amount-out, amount-in, balance

# We generate balance assertions by assigning to "balance"
# above, but you may sometimes need to remove these because:
#
# - the CSV balance differs from the true balance,
#   by up to 0.0000000000005 in my experience
#
# - it is sometimes calculated based on non-chronological ordering,
#   eg when multiple transactions clear on the same day

# date is in UK/Ireland format
date-format  %d/%m/%Y

# set the currency
currency  EUR

# set the base account for all txns
account1  assets:bank:boi:checking
$ hledger -f bankofireland-checking.csv print
2012-12-07 LODGMENT       529898
    assets:bank:boi:checking         EUR10.0 = EUR131.2
    income:unknown                  EUR-10.0

2012-12-07 PAYMENT
    assets:bank:boi:checking         EUR-5.0 = EUR126.0
    expenses:unknown                  EUR5.0

The balance assertions don't raise an error above, because we're reading directly from CSV, but they will be checked if these entries are imported into a journal file.

Coinbase

A simple example with some CSV from Coinbase. The spot price is recorded using cost notation. The legacy amount field name conveniently sets amount 2 (posting 2's amount) to the total cost.

# Timestamp,Transaction Type,Asset,Quantity Transacted,Spot Price Currency,Spot Price at Transaction,Subtotal,Total (inclusive of fees and/or spread),Fees and/or Spread,Notes
# 2021-12-30T06:57:59Z,Receive,USDC,100,GBP,0.740000,"","","","Received 100.00 USDC from an external account"
# coinbase.csv.rules
skip         1
fields       Timestamp,Transaction_Type,Asset,Quantity_Transacted,Spot_Price_Currency,Spot_Price_at_Transaction,Subtotal,Total,Fees_Spread,Notes
date         %Timestamp
date-format  %Y-%m-%dT%T%Z
description  %Notes
account1     assets:coinbase:cc
amount       %Quantity_Transacted %Asset @ %Spot_Price_at_Transaction %Spot_Price_Currency
$ hledger print -f coinbase.csv
2021-12-30 Received 100.00 USDC from an external account
    assets:coinbase:cc    100 USDC @ 0.740000 GBP
    income:unknown                 -74.000000 GBP

Amazon

Here we convert amazon.com order history, and use an if block to generate a third posting if there's a fee. (In practice you'd probably get this data from your bank instead, but it's an example.)

"Date","Type","To/From","Name","Status","Amount","Fees","Transaction ID"
"Jul 29, 2012","Payment","To","Foo.","Completed","$20.00","$0.00","16000000000000DGLNJPI1P9B8DKPVHL"
"Jul 30, 2012","Payment","To","Adapteva, Inc.","Completed","$25.00","$1.00","17LA58JSKRD4HDGLNJPI1P9B8DKPVHL"
# amazon-orders.csv.rules

# skip one header line
skip 1

# name the csv fields, and assign the transaction's date, amount and code.
# Avoided the "status" and "amount" hledger field names to prevent confusion.
fields date, _, toorfrom, name, amzstatus, amzamount, fees, code

# how to parse the date
date-format %b %-d, %Y

# combine two fields to make the description
description %toorfrom %name

# save the status as a tag
comment     status:%amzstatus

# set the base account for all transactions
account1    assets:amazon
# leave amount1 blank so it can balance the other(s).
# I'm assuming amzamount excludes the fees, don't remember

# set a generic account2
account2    expenses:misc
amount2     %amzamount
# and maybe refine it further:
#include categorisation.rules

# add a third posting for fees, but only if they are non-zero.
if %fees [1-9]
 account3    expenses:fees
 amount3     %fees
$ hledger -f amazon-orders.csv print
2012-07-29 (16000000000000DGLNJPI1P9B8DKPVHL) To Foo.  ; status:Completed
    assets:amazon
    expenses:misc          $20.00

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

Paypal

Here's a real-world rules file for (customised) Paypal CSV, with some Paypal-specific rules, and a second rules file included:

"Date","Time","TimeZone","Name","Type","Status","Currency","Gross","Fee","Net","From Email Address","To Email Address","Transaction ID","Item Title","Item ID","Reference Txn ID","Receipt ID","Balance","Note"
"10/01/2019","03:46:20","PDT","Calm Radio","Subscription Payment","Completed","USD","-6.99","0.00","-6.99","[email protected]","[email protected]","60P57143A8206782E","MONTHLY - $1 for the first 2 Months: Me - Order 99309. Item total: $1.00 USD first 2 months, then $6.99 / Month","","I-R8YLY094FJYR","","-6.99",""
"10/01/2019","03:46:20","PDT","","Bank Deposit to PP Account ","Pending","USD","6.99","0.00","6.99","","[email protected]","0TU1544T080463733","","","60P57143A8206782E","","0.00",""
"10/01/2019","08:57:01","PDT","Patreon","PreApproved Payment Bill User Payment","Completed","USD","-7.00","0.00","-7.00","[email protected]","[email protected]","2722394R5F586712G","Patreon* Membership","","B-0PG93074E7M86381M","","-7.00",""
"10/01/2019","08:57:01","PDT","","Bank Deposit to PP Account ","Pending","USD","7.00","0.00","7.00","","[email protected]","71854087RG994194F","Patreon* Membership","","2722394R5F586712G","","0.00",""
"10/19/2019","03:02:12","PDT","Wikimedia Foundation, Inc.","Subscription Payment","Completed","USD","-2.00","0.00","-2.00","[email protected]","[email protected]","K9U43044RY432050M","Monthly donation to the Wikimedia Foundation","","I-R5C3YUS3285L","","-2.00",""
"10/19/2019","03:02:12","PDT","","Bank Deposit to PP Account ","Pending","USD","2.00","0.00","2.00","","[email protected]","3XJ107139A851061F","","","K9U43044RY432050M","","0.00",""
"10/22/2019","05:07:06","PDT","Noble Benefactor","Subscription Payment","Completed","USD","10.00","-0.59","9.41","[email protected]","[email protected]","6L8L1662YP1334033","Joyful Systems","","I-KC9VBGY2GWDB","","9.41",""
# paypal-custom.csv.rules

# Tips:
# Export from Activity -> Statements -> Custom -> Activity download
# Suggested transaction type: "Balance affecting"
# Paypal's default fields in 2018 were:
# "Date","Time","TimeZone","Name","Type","Status","Currency","Gross","Fee","Net","From Email Address","To Email Address","Transaction ID","Shipping Address","Address Status","Item Title","Item ID","Shipping and Handling Amount","Insurance Amount","Sales Tax","Option 1 Name","Option 1 Value","Option 2 Name","Option 2 Value","Reference Txn ID","Invoice Number","Custom Number","Quantity","Receipt ID","Balance","Address Line 1","Address Line 2/District/Neighborhood","Town/City","State/Province/Region/County/Territory/Prefecture/Republic","Zip/Postal Code","Country","Contact Phone Number","Subject","Note","Country Code","Balance Impact"
# This rules file assumes the following more detailed fields, configured in "Customize report fields":
# "Date","Time","TimeZone","Name","Type","Status","Currency","Gross","Fee","Net","From Email Address","To Email Address","Transaction ID","Item Title","Item ID","Reference Txn ID","Receipt ID","Balance","Note"

fields date, time, timezone, description_, type, status_, currency, grossamount, feeamount, netamount, fromemail, toemail, code, itemtitle, itemid, referencetxnid, receiptid, balance, note

skip  1

date-format  %-m/%-d/%Y

# ignore some paypal events
if
In Progress
Temporary Hold
Update to
 skip

# add more fields to the description
description %description_ %itemtitle

# save some other fields as tags
comment  itemid:%itemid, fromemail:%fromemail, toemail:%toemail, time:%time, type:%type, status:%status_

# convert to short currency symbols
if %currency USD
 currency $
if %currency EUR
 currency E
if %currency GBP
 currency P

# generate postings

# the first posting will be the money leaving/entering my paypal account
# (negative means leaving my account, in all amount fields)
account1 assets:online:paypal
amount1  %netamount

# the second posting will be money sent to/received from other party
# (account2 is set below)
amount2  -%grossamount

# if there's a fee, add a third posting for the money taken by paypal.
if %feeamount [1-9]
 account3 expenses:banking:paypal
 amount3  -%feeamount
 comment3 business:

# choose an account for the second posting

# override the default account names:
# if the amount is positive, it's income (a debit)
if %grossamount ^[^-]
 account2 income:unknown
# if negative, it's an expense (a credit)
if %grossamount ^-
 account2 expenses:unknown

# apply common rules for setting account2 & other tweaks
include common.rules

# apply some overrides specific to this csv

# Transfers from/to bank. These are usually marked Pending,
# which can be disregarded in this case.
if
Bank Account
Bank Deposit to PP Account
 description %type for %referencetxnid %itemtitle
 account2 assets:bank:wf:pchecking
 account1 assets:online:paypal

# Currency conversions
if Currency Conversion
 account2 equity:currency conversion
# common.rules

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

if
Calm Radio
 account2 expenses:online:apps

if
electronic frontier foundation
Patreon
wikimedia
Advent of Code
 account2 expenses:dues

if Google
 account2 expenses:online:apps
 description google | music
$ hledger -f paypal-custom.csv  print
2019-10-01 (60P57143A8206782E) Calm Radio MONTHLY - $1 for the first 2 Months: Me - Order 99309. Item total: $1.00 USD first 2 months, then $6.99 / Month  ; itemid:, fromemail:[email protected], toemail:[email protected], time:03:46:20, type:Subscription Payment, status:Completed
    assets:online:paypal          $-6.99 = $-6.99
    expenses:online:apps           $6.99

2019-10-01 (0TU1544T080463733) Bank Deposit to PP Account for 60P57143A8206782E  ; itemid:, fromemail:, toemail:[email protected], time:03:46:20, type:Bank Deposit to PP Account, status:Pending
    assets:online:paypal               $6.99 = $0.00
    assets:bank:wf:pchecking          $-6.99

2019-10-01 (2722394R5F586712G) Patreon Patreon* Membership  ; itemid:, fromemail:[email protected], toemail:[email protected], time:08:57:01, type:PreApproved Payment Bill User Payment, status:Completed
    assets:online:paypal          $-7.00 = $-7.00
    expenses:dues                  $7.00

2019-10-01 (71854087RG994194F) Bank Deposit to PP Account for 2722394R5F586712G Patreon* Membership  ; itemid:, fromemail:, toemail:[email protected], time:08:57:01, type:Bank Deposit to PP Account, status:Pending
    assets:online:paypal               $7.00 = $0.00
    assets:bank:wf:pchecking          $-7.00

2019-10-19 (K9U43044RY432050M) Wikimedia Foundation, Inc. Monthly donation to the Wikimedia Foundation  ; itemid:, fromemail:[email protected], toemail:[email protected], time:03:02:12, type:Subscription Payment, status:Completed
    assets:online:paypal             $-2.00 = $-2.00
    expenses:dues                     $2.00
    expenses:banking:paypal      ; business:

2019-10-19 (3XJ107139A851061F) Bank Deposit to PP Account for K9U43044RY432050M  ; itemid:, fromemail:, toemail:[email protected], time:03:02:12, type:Bank Deposit to PP Account, status:Pending
    assets:online:paypal               $2.00 = $0.00
    assets:bank:wf:pchecking          $-2.00

2019-10-22 (6L8L1662YP1334033) Noble Benefactor Joyful Systems  ; itemid:, fromemail:[email protected], toemail:[email protected], time:05:07:06, type:Subscription Payment, status:Completed
    assets:online:paypal                       $9.41 = $9.41
    revenues:foss donations:darcshub         $-10.00  ; business:
    expenses:banking:paypal                    $0.59  ; business:

Timeclock

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

timedot format is hledger's human-friendly time logging format. Compared to timeclock format, it is more convenient for quick, approximate, and retroactive time logging, and more human-readable (you can see at a glance where time was spent). A quick example:

2023-05-01
hom:errands          .... ....  ; two hours; the space is ignored
fos:hledger:timedot  ..         ; half an hour
per:admin:finance               ; no time spent yet

hledger reads this as a transaction on this day with three (unbalanced) postings, where each dot represents "0.25". No commodity symbol is assumed, but we typically interpret it as hours.

$ hledger -f a.timedot print   # .timedot file extension (or timedot: prefix) is required
2023-05-01 *
    (hom:errands)                    2.00  ; two hours
    (fos:hledger:timedot)            0.50  ; half an hour
    (per:admin:finance)                 0

A timedot file contains a series of transactions (usually one per day). Each begins with a simple date (Y-M-D, Y/M/D, or Y.M.D), optionally be followed on the same line by a transaction description, and/or a transaction comment following a semicolon.

After the date line are zero or more time postings, consisting of:

  • An account name - any hledger-style account name, optionally indented.

  • Two or more spaces - required if there is an amount (as in journal format).

  • A timedot amount, which can be

    • empty (representing zero)

    • a number, optionally followed by a unit s, m, h, d, w, mo, 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

Numbers:

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

Dots:

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

2016/2/2
inc:client1   .... ....
biz:research  .
$ hledger -f a.timedot print date:2016/2/2
2016-02-02 *
    (inc:client1)          2.00

2016-02-02 *
    (biz:research)          0.25
$ hledger -f a.timedot bal --daily --tree
Balance changes in 2016-02-01-2016-02-03:

            ||  2016-02-01d  2016-02-02d  2016-02-03d 
============++========================================
 biz        ||         0.25         0.25         1.00 
   research ||         0.25         0.25         1.00 
 fos        ||         1.50            0         3.00 
   haskell  ||         1.50            0            0 
   hledger  ||            0            0         3.00 
 inc        ||         6.00         2.00         4.00 
   client1  ||         6.00         2.00         4.00 
------------++----------------------------------------
            ||         7.75         2.25         8.00 

Letters:

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

2023-11-01
work:adm  ccecces
$ hledger -f a.timedot print
2023-11-01
    (work:adm)  1     ; t:c
    (work:adm)  0.5   ; t:e
    (work:adm)  0.25  ; t:s
$ hledger -f a.timedot bal
                1.75  work:adm
--------------------
                1.75  
$ hledger -f a.timedot bal --pivot t
                1.00  c
                0.50  e
                0.25  s
--------------------
                1.75  

Org:

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

Using . as account name separator:

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

PART 3: REPORTING CONCEPTS

Amount formatting

Commodity display style

For the amounts in each commodity, hledger chooses a consistent display style (symbol placement, decimal mark and digit group marks, number of decimal digits) to use in most reports. This is inferred as follows:

First, if there's a D directive declaring a default commodity, that commodity symbol and amount format is applied to all no-symbol amounts in the journal.

Then each commodity's display style is determined from its commodity directive. We recommend always declaring commodities with commodity directives, since they help ensure consistent display styles and precisions, and bring other benefits such as error checking for commodity symbols. Here's an example:

# Set display styles (and decimal marks, for parsing, if there is no decimal-mark directive)
# for the $, EUR, INR and no-symbol commodities:
commodity $1,000.00
commodity EUR 1.000,00
commodity INR 9,99,99,999.00
commodity 1 000 000.9455

But for convenience, if a commodity directive is not present, hledger infers a commodity's display styles from its amounts as they are written in the journal (excluding cost amounts and amounts in periodic transaction rules or auto posting rules). It uses

  • the symbol placement and decimal mark of the first amount seen
  • the digit group marks of the first amount with digit group marks
  • and the maximum number of decimal digits seen across all amounts.

And as fallback if no applicable amounts are found, it would use a default style, like $1000.00 (symbol on the left with no space, period as decimal mark, and two decimal digits).

Finally, commodity styles can be overridden by the -c/--commodity-style command line option.

Rounding

Amounts are stored internally as decimal numbers with up to 255 decimal places. They are displayed with their original journal precisions by print and print-like reports, and rounded to their display precision (the number of decimal digits specified by the commodity display style) by other reports. When rounding, hledger uses banker's rounding (it rounds to the nearest even digit). So eg 0.5 displayed with zero decimal digits appears as "0".

Trailing decimal marks

If you're wondering why your print report sometimes shows trailing decimal marks, with no decimal digits; it does this when showing amounts that have digit group marks but no decimal digits, to disambiguate them and allow them to be re-parsed reliably (see Decimal marks). Eg:

commodity $1,000.00

2023-01-02
    (a)      $1000
$ hledger print
2023-01-02
    (a)        $1,000.

If this is a problem (eg when exporting to Ledger), you can avoid it by disabling digit group marks, eg with -c/--commodity (for each affected commodity):

$ hledger print -c '$1000.00'
2023-01-02
    (a)          $1000

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

$ hledger print -c '$1,000.00' --round=soft
2023-01-02
    (a)      $1,000.00

Amount parseability

More generally, hledger output falls into three rough categories, which format amounts a little bit differently to suit different consumers:

1. "hledger-readable output" - should be readable by hledger (and by humans)

  • This is produced by reports that show full journal entries: print, import, close, 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).

Examples:

-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 -)
date:..12/1
date:thismonth..
date:thismonth

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"
-p2009/1/1to2009/4/1
-p2009/1/1..2009/4/1

Dates are smart dates, so if the current year is 2009, these are also equivalent to the above:

-p "1/1 4/1"
-p "jan-apr"
-p "this year to 4/1"

If you specify only one date, the missing start or end date will be the earliest or latest transaction date in the journal:

-p "from 2009/1/1"everything after january 1, 2009
-p "since 2009/1"the same, since is a synonym
-p "from 2009"the same
-p "to 2009"everything before january 1, 2009

You can also specify a period by writing a single partial or full date:

-p "2009"the year 2009; equivalent to "2009/1/1 to 2010/1/1"
-p "2009/1"the month of january 2009; equivalent to "2009/1/1 to 2009/2/1"
-p "2009/1/1"the first day of 2009; equivalent to "2009/1/1 to 2009/1/2"

or by using the "Q" quarter-year syntax (case insensitive):

-p "2009Q1"first quarter of 2009, equivalent to "2009/1/1 to 2009/4/1"
-p "q4"fourth quarter of the current year

Period expressions with a report interval

A period expression can also begin with a report interval, separated from the start/end dates (if any) by a space or the word in:

-p "weekly from 2009/1/1 to 2009/4/1"
-p "monthly in 2008"
-p "quarterly"

More complex report intervals

Some more complex intervals can be specified within period expressions, such as:

  • biweekly (every two weeks)
  • 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)

Examples:

-p "bimonthly from 2008"
-p "every 2 weeks"
-p "every 5 months from 2009/03"
-p "every 2nd day of week"periods will go from Tue to Tue
-p "every Tue"same
-p "every 15th day"period boundaries will be on 15th of each month
-p "every 2nd Monday"period boundaries will be on second Monday of each month
-p "every 11/05"yearly periods with boundaries on 5th of November
-p "every 5th November"same
-p "every Nov 5th"same

Show historical balances at end of the 15th day of each month (N is an end date, exclusive as always):

$ hledger balance -H -p "every 16th day"

Group postings from the start of wednesday to end of the following tuesday (N is both (inclusive) start date and (exclusive) end date):

$ hledger register checking -p "every 3rd day of week"

Multiple weekday intervals

This special form is also supported:

  • every WEEKDAYNAME,WEEKDAYNAME,... (full or three-letter english weekday names, case insensitive)

Also, weekday and weekendday are shorthand for mon,tue,wed,thu,fri and sat,sun.

This is mainly intended for use with --forecast, to generate periodic transactions on arbitrary days of the week. It may be less useful with -p, since it divides each week into subperiods of unequal length, which is unusual. (Related: #1632)

Examples:

-p "every mon,wed,fri"dates will be Mon, Wed, Fri;
periods will be Mon-Tue, Wed-Thu, Fri-Sun
-p "every weekday"dates will be Mon, Tue, Wed, Thu, Fri;
periods will be Mon, Tue, Wed, Thu, Fri-Sun
-p "every weekendday"dates will be Sat, Sun;
periods will be Sat, Sun-Fri

Depth

With the --depth NUM option (short form: -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.

Queries

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:

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

    '^expenses\b'
    'food$'
    'fuel|repair'
    'accounts (payable|receivable)'

  • To match something other than account name, add one of the query type prefixes described in "Query types" below:

    date:202312-
    status:
    desc:amazon
    cur:USD
    cur:\\$
    amt:'>0'

  • Add a not: prefix to negate a term:

    not:status:'*'
    not:desc:'opening|closing'
    not:cur:USD

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

acct:REGEX or REGEX
Match account names containing this case insensitive regular expression. This is the default query type, so we usually don't bother writing the "acct:" prefix.

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

code:REGEX
Match by transaction code (eg check number).

cur:REGEX
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:\\$.

desc:REGEX
Match transaction descriptions.

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

date2:PERIODEXPR
Match secondary dates within the specified period (independent of the --date2 flag).

depth:N
Match (or display, depending on command) accounts at or above this depth.

expr:"TERM AND NOT (TERM OR TERM)" (eg)
Match with a boolean combination of queries (which must be enclosed in quotes). See Combining query terms below.

note:REGEX
Match transaction notes (the part of the description right of |, or the whole description if there's no |).

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

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

tag:REGEX[=REGEX]
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.

(inacct:ACCTNAME
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.)

Pivoting

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

Pivoted balance report, using member: tag values instead:

$ hledger balance --pivot member
               2 EUR
              -2 EUR  John Doe
--------------------
                   0

One way to show only amounts with a member: value (using a query):

$ hledger balance --pivot member tag:member=.
              -2 EUR  John Doe
--------------------
              -2 EUR

Another way (the acct: query matches against the pivoted "account name"):

$ hledger balance --pivot member acct:.
              -2 EUR  John Doe
--------------------
              -2 EUR

Hierarchical reports can be generated with multiple 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

Forecasting, or speculative future reporting, can be useful for estimating future balances, or for exploring different future scenarios.

The simplest and most flexible way to do it with hledger is to manually record a bunch of future-dated transactions. You could keep these in a separate future.journal and include that with -f only when you want to see them.

--forecast

There is another way: with the --forecast option, hledger can generate temporary "forecast transactions" for reporting purposes, according to periodic transaction rules defined in the journal. Each rule can generate multiple recurring transactions, so by changing one rule you can change many forecasted transactions.

Forecast transactions usually start after ordinary transactions end. By default, they begin after your latest-dated ordinary transaction, or today, whichever is later, and they end six months from today. (The exact rules are a little more complicated, and are given below.)

This is the "forecast period", which need not be the same as the report period. You can override it - eg to forecast farther into the future, or to force forecast transactions to overlap your ordinary transactions - by giving the --forecast option a period expression argument, like --forecast=..2099 or --forecast=2023-02-15... Note that the = is required.

Inspecting forecast transactions

print is the best command for inspecting and troubleshooting forecast transactions. Eg:

~ monthly from 2022-12-20    rent
    assets:bank:checking
    expenses:rent           $1000
$ hledger print --forecast --today=2023/4/21
2023-05-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    assets:bank:checking
    expenses:rent                  $1000

2023-06-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    assets:bank:checking
    expenses:rent                  $1000

2023-07-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    assets:bank:checking
    expenses:rent                  $1000

2023-08-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    assets:bank:checking
    expenses:rent                  $1000

2023-09-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    assets:bank:checking
    expenses:rent                  $1000

Here there are no ordinary transactions, so the forecasted transactions begin on the first 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).

Budgeting

With the balance command's --budget report, each periodic transaction rule generates recurring budget goals in specified accounts, and goals and actual performance can be compared. See the balance command's doc below.

You can generate budget goals and forecast transactions at the same time, from the same or different periodic transaction rules: hledger bal -M --budget --forecast ...

See also: Budgeting and Forecasting.

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

2022-01-01
  assets:dollars    $-135
  assets:euros       €100 @ $1.35   ; $1.35 per euro (unit cost)

Variant 2

2022-01-01
  assets:dollars    $-135
  assets:euros       €100 @@ $135   ; $135 total cost

Typically, writing the unit cost (variant 1) is preferable; it can be more effort, requiring more attention to decimal digits; but it reveals the per-unit cost basis, and makes stock sales easier.

Costs can also be left implicit, and hledger will infer the cost that is consistent with a balanced transaction:

Variant 3

2022-01-01
  assets:dollars    $-135
  assets:euros       €100

Here, hledger will attach a @@ €100 cost to the first amount (you can see it with hledger print -x). This form looks convenient, but there are downsides:

  • It sacrifices some error checking. For example, if you accidentally wrote €10 instead of €100, hledger would not be able to detect the mistake.

  • It is sensitive to the order of postings - if they were reversed, a different entry would be inferred and reports would be different.

  • The per-unit cost basis is not easy to read.

So generally this kind of entry is not recommended. You can make sure you have none of these by using -s (strict mode), or by running hledger check balanced.

Reporting at cost

Now when you add the -B/--cost flag to reports ("B" is from Ledger's -B/--basis/--cost flag), any amounts which have been annotated with costs will be converted to their cost's commodity (in the report output). Ie they will be displayed "at cost" or "at sale price".

Some things to note:

  • Costs are attached to specific posting amounts in specific transactions, and once recorded they do not change. This contrasts with market prices, which are ambient and fluctuating.

  • Conversion to cost is performed before conversion to market value (described below).

Equity conversion postings

There is a problem with the entries above - they are not conventional Double Entry Bookkeeping (DEB) notation, and because of the "magical" transformation of one commodity into another, they cause an imbalance in the Accounting Equation. This shows up as a non-zero grand total in balance reports like hledger bse.

For most hledger users, this doesn't matter in practice and can safely be ignored ! But if you'd like to learn more, keep reading.

Conventional DEB uses an extra pair of equity postings to balance the transaction. Of course you can do this in hledger as well:

Variant 4

2022-01-01
    assets:dollars      $-135
    assets:euros         €100
    equity:conversion    $135
    equity:conversion   €-100

Now the transaction is perfectly balanced according to standard DEB, and hledger bse's total will not be disrupted.

And, hledger can still infer the cost for cost reporting, but it's not done by default - you must add the --infer-costs flag like so:

$ hledger print --infer-costs
2022-01-01 one hundred euros purchased at $1.35 each
    assets:dollars       $-135 @@ €100
    assets:euros                  €100
    equity:conversion             $135
    equity:conversion            €-100
$ hledger bal --infer-costs -B
               €-100  assets:dollars                                                                                                                                              
                €100  assets:euros                                                                                                                                                
--------------------                                                                                                                                                              
                   0                                                                                                                                                              

Here are some downsides of this kind of entry:

  • The per-unit cost basis is not easy to read.

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

2022-01-01
  assets:dollars  -$135
  assets:euros     €100 @ $1.35
$ hledger print --infer-equity
2022-01-01
    assets:dollars                    $-135
    assets:euros               €100 @ $1.35
    equity:conversion:$-€:€           €-100
    equity:conversion:$-€:$         $135.00

The equity account names will be "equity:conversion:A-B:A" and "equity:conversion:A-B:B" where A is the alphabetically first commodity symbol. You can customise the "equity:conversion" part by declaring an account with the V/Conversion account type.

Combining costs and equity conversion postings

Finally, you can use both the @/@@ cost notation and equity postings at the same time. This in theory gives the best of all worlds - preserving the accounting equation, revealing the per-unit cost basis, and providing more flexibility in how you write the entry:

Variant 5

2022-01-01 one hundred euros purchased at $1.35 each
    assets:dollars      $-135
    equity:conversion    $135
    equity:conversion   €-100
    assets:euros         €100 @ $1.35

All the other variants above can (usually) be rewritten to this final form with:

$ hledger print -x --infer-costs --infer-equity

Downsides:

  • The precise format of the journal entry becomes more important. If hledger can't detect and match up the cost and equity postings, it will give a transaction balancing error.

  • The add command does not yet accept this kind of entry (#2056).

  • This is the most verbose form.

Requirements for detecting equity conversion postings

--infer-costs has certain requirements (unlike --infer-equity, which always works). It will infer costs only in transactions with:

  • Two non-equity postings, in different commodities. Their order is significant: the cost will be added to the first of them.

  • Two postings to equity conversion accounts, next to one another, which balance the two non-equity postings. This balancing is checked to the same precision (number of decimal places) used in the conversion posting's amount. Equity conversion accounts are:

    • any accounts declared with account type V/Conversion, or their subaccounts
    • otherwise, accounts named equity:conversion, equity:trade, 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
2016/11/3
    assets:euros        €100
    assets:checking

; the euro is worth fewer dollars by dec 21
P 2016/12/21 € $1.03

How many euros do I have ?

$ hledger -f t.j bal -N euros
                €100  assets:euros

What are they worth at end of nov 3 ?

$ hledger -f t.j bal -N euros -V -e 2016/11/4
             $110.00  assets:euros

What are they worth after 2016/12/21 ? (no report end date specified, defaults to today)

$ hledger -f t.j bal -N euros -V
             $103.00  assets:euros

Here are some examples showing the effect of --value, as seen with print:

P 2000-01-01 A  1 B
P 2000-02-01 A  2 B
P 2000-03-01 A  3 B
P 2000-04-01 A  4 B

2000-01-01
  (a)      1 A @ 5 B

2000-02-01
  (a)      1 A @ 6 B

2000-03-01
  (a)      1 A @ 7 B

Show the cost of each posting:

$ hledger -f- print --cost
2000-01-01
    (a)             5 B

2000-02-01
    (a)             6 B

2000-03-01
    (a)             7 B

Show the value as of the last day of the report period (2000-02-29):

$ hledger -f- print --value=end date:2000/01-2000/03
2000-01-01
    (a)             2 B

2000-02-01
    (a)             2 B

With no report period specified, that shows the value as of the last day of the journal (2000-03-01):

$ hledger -f- print --value=end
2000-01-01
    (a)             3 B

2000-02-01
    (a)             3 B

2000-03-01
    (a)             3 B

Show the current value (the 2000-04-01 price is still in effect today):

$ hledger -f- print --value=now
2000-01-01
    (a)             4 B

2000-02-01
    (a)             4 B

2000-03-01
    (a)             4 B

Show the value on 2000/01/15:

$ hledger -f- print --value=2000-01-15
2000-01-01
    (a)             1 B

2000-02-01
    (a)             1 B

2000-03-01
    (a)             1 B

Interaction of valuation and queries

When matching postings based on queries in the presence of valuation, the following happens:

  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
print
posting amountscostvalue at report end or todayvalue at posting datevalue at report or journal endvalue at DATE/today
balance assertions/assignmentsunchangedunchangedunchangedunchangedunchanged

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

PART 4: COMMANDS

Commands overview

Here are the built-in commands:

DATA ENTRY

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

DATA CREATION

  • close - generate balance-zeroing/restoring transactions
  • rewrite - generate auto postings, like print --auto

DATA MANAGEMENT

  • check - check for various kinds of error in the data
  • diff - compare account transactions in two journal files

REPORTS, FINANCIAL

REPORTS, VERSATILE

  • 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

REPORTS, BASIC

  • 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

  • help - show the hledger manual with info/man/pager
  • demo - show small hledger demos in the terminal

ADD-ONS

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.

accounts

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.

Examples:

$ hledger accounts
assets:bank:checking
assets:bank:saving
assets:cash
expenses:food
expenses:supplies
income:gifts
income:salary
liabilities:debts
$ hledger accounts --undeclared --directives >> $LEDGER_FILE
$ hledger check accounts

activity

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.

Examples:

$ hledger activity --quarterly
2008-01-01 **
2008-04-01 *******
2008-07-01 
2008-10-01 **

add

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.

Features:

  • add tries to provide useful defaults, using the most similar (by description) recent transaction (filtered by the query, if any) as a template.
  • You can also set the initial defaults with command line arguments.
  • Readline-style edit keys can be used during data entry.
  • The tab key will auto-complete whenever possible - accounts, payees/descriptions, dates (yesterday, today, tomorrow). If the input area is empty, it will insert the default value.
  • 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]: 
Saved.
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.

aregister

(areg)

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.

balance

(bal)

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)

..in..

..either..

  • per period (the default)
  • or accumulated since report start date (--cumulative)
  • or accumulated since account creation (--historical/-H)

..possibly converted to..

..with..

  • totals (-T), averages (-A), percentages (-%), inverted sign (--invert)
  • rows and columns swapped (--transpose)
  • another field used as account name (--pivot)
  • custom-formatted line items (single-period reports only) (--format)
  • commodities displayed on the same line or multiple lines (--layout)

This command supports the output destination and output format options, with output formats txt, csv, tsv (Added in 1.32), json, and (multi-period reports only:) html. In txt output in a colour-supporting terminal, negative amounts are shown in red.

Simple balance report

With no arguments, balance shows a list of all accounts and their change of balance - ie, the sum of posting amounts, both inflows and outflows - during the entire period of the journal. ("Simple" here means just one column of numbers, covering a single period. You can also have multi-period reports, described later.)

For real-world accounts, these numbers will normally be their end balance at the end of the journal period; more on this below.

Accounts are sorted by declaration order if any, and then alphabetically by account name. For instance (using examples/sample.journal):

$ hledger -f examples/sample.journal bal
                  $1  assets:bank:saving
                 $-2  assets:cash
                  $1  expenses:food
                  $1  expenses:supplies
                 $-1  income:gifts
                 $-1  income:salary
                  $1  liabilities:debts
--------------------
                   0  

Accounts with a zero balance (and no non-zero subaccounts, in tree mode - see below) are hidden by default. Use -E/--empty to show them (revealing assets:bank:checking here):

$ hledger -f examples/sample.journal bal  -E
                   0  assets:bank:checking
                  $1  assets:bank:saving
                 $-2  assets:cash
                  $1  expenses:food
                  $1  expenses:supplies
                 $-1  income:gifts
                 $-1  income:salary
                  $1  liabilities:debts
--------------------
                   0  

The total of the amounts displayed is shown as the last line, unless -N/--no-total is used.

Balance report line format

For single-period balance reports displayed in the terminal (only), you can use --format FMT to customise the format and content of each line. Eg:

$ hledger -f examples/sample.journal balance --format "%20(account) %12(total)"
              assets          $-1
         bank:saving           $1
                cash          $-2
            expenses           $2
                food           $1
            supplies           $1
              income          $-2
               gifts          $-1
              salary          $-1
   liabilities:debts           $1
---------------------------------
                                0

The FMT format string specifies the formatting applied to each account/balance pair. It may contain any suitable text, with data fields interpolated like so:

%[MIN][.MAX](FIELDNAME)

  • MIN pads with spaces to at least this width (optional)

  • MAX truncates at this width (optional)

  • FIELDNAME must be enclosed in parentheses, and can be one of:

    • depth_spacer - a number of spaces equal to the account's depth, or if MIN is specified, MIN * depth spaces.
    • account - the account's 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
--------------------
                 $-2  

List or tree mode

By default, or with -l/--flat, accounts are shown as a flat list with their full names visible, as in the examples above.

With -t/--tree, the account hierarchy is shown, with subaccounts' "leaf" names indented below their parent:

$ hledger -f examples/sample.journal balance
                 $-1  assets
                  $1    bank:saving
                 $-2    cash
                  $2  expenses
                  $1    food
                  $1    supplies
                 $-2  income
                 $-1    gifts
                 $-1    salary
                  $1  liabilities:debts
--------------------
                   0

Notes:

  • "Boring" accounts are combined with their subaccount for more compact output, unless --no-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
--------------------
                   0  

Dropping top-level accounts

You can also hide one or more top-level account name parts, using --drop NUM. This can be useful for hiding repetitive top-level account names:

$ hledger -f examples/sample.journal bal expenses --drop 1
                  $1  food
                  $1  supplies
--------------------
                  $2  

Showing declared accounts

With --declared, accounts which have been declared with an account directive will be included in the balance report, even if they have no transactions. (Since they will have a zero balance, you will also need -E/--empty to see them.)

More precisely, leaf declared accounts (with no subaccounts) will be included, since those are usually the more useful in reports.

The idea of this is to be able to see a useful "complete" balance report, even when you don't have transactions in all of your declared accounts yet.

Sorting by amount

With -S/--sort-amount, accounts with the largest (most positive) balances are shown first. Eg: hledger bal expenses -MAS shows your biggest averaged monthly expenses first. When more than one commodity is present, they will be sorted by the alphabetically earliest commodity first, and then by subsequent commodities (if an amount is missing a commodity, it is treated as 0).

Revenues and liability balances are typically negative, however, so -S shows these in reverse order. To work around this, you can add --invert to flip the signs. (Or, use one of the higher-level reports, which flip the sign automatically. Eg: hledger incomestatement -MAS).

Percentages

With -%/--percent, balance reports show each account's value expressed as a percentage of the (column) total.

Note it is not useful to calculate percentages if the amounts in a column have mixed signs. In this case, make a separate report for each sign, eg:

$ hledger bal -% amt:`>0`
$ hledger bal -% amt:`<0`

Similarly, if the amounts in a column have mixed commodities, convert them to one commodity with -B, -V, -X or --value, or make a separate report for each commodity:

$ hledger bal -% cur:\\$
$ hledger bal -% cur:€

Multi-period balance report

With a report interval (set by the -D/--daily, -W/--weekly, -M/--monthly, -Q/--quarterly, -Y/--yearly, or -p/--period flag), balance shows a tabular report, with columns representing successive time periods (and a title):

$ hledger -f examples/sample.journal bal --quarterly income expenses -E
Balance changes in 2008:

                   ||  2008q1  2008q2  2008q3  2008q4 
===================++=================================
 expenses:food     ||       0      $1       0       0 
 expenses:supplies ||       0      $1       0       0 
 income:gifts      ||       0     $-1       0       0 
 income:salary     ||     $-1       0       0       0 
-------------------++---------------------------------
                   ||     $-1      $1       0       0 

Notes:

  • The report's start/end dates will be expanded, if necessary, to fully encompass the displayed subperiods (so that the first and last subperiods have the same duration as the others).
  • Leading and trailing periods (columns) containing all zeroes are not shown, unless -E/--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:

hledger balance [CALCULATIONTYPE] [ACCUMULATIONTYPE] [VALUATIONTYPE] ...

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:

Valuation:>
Accumulation:v
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
2017-11-01
  income                   $-1950
  expenses:bus                $35
  expenses:food:groceries    $310
  expenses:food:dining        $42
  expenses:movies             $38
  assets:bank:checking

2017-12-01
  income                   $-2100
  expenses:bus                $53
  expenses:food:groceries    $380
  expenses:food:dining        $32
  expenses:gifts             $100
  assets:bank:checking

we can see a budget report like this:

$ hledger bal -M --budget
Budget performance in 2017-11-01..2017-12-31:

               ||                  Nov                   Dec 
===============++============================================
 <unbudgeted>  || $-425                 $-565                
 expenses      ||  $425 [ 99% of $430]   $565 [131% of $430] 
 expenses:bus  ||   $35 [117% of  $30]    $53 [177% of  $30] 
 expenses:food ||  $352 [ 88% of $400]   $412 [103% of $400] 
---------------++--------------------------------------------
               ||     0 [  0% of $430]      0 [  0% of $430] 

This is "goal-based budgeting"; you define goals for accounts and periods, often recurring, and hledger shows performance relative to the goals. This contrasts with "envelope budgeting", which is more detailed and strict - useful when cash is tight, but also quite a bit more work. https://plaintextaccounting.org/Budgeting has more on this topic.

Using the budget report

Historically this report has been confusing and fragile. hledger's version should be relatively robust and intuitive, but you may still find surprises. Here are more notes to help with learning and troubleshooting.

  • In the above example, expenses: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

2020-01-15
  expenses:food    $400
  assets:checking
$ hledger bal --budget expenses
Budget performance in 2020-01-15:

               ||         2020-01-15 
===============++====================
 <unbudgeted>  || $400               
 expenses:food ||    0 [ 0% of $500] 
---------------++--------------------
               || $400 [80% of $500] 

In this case, the budget goal transactions are generated on first days of of month (this can be seen with hledger print --forecast tag:generated expenses). Whereas the report period defaults to just the 15th day of january (this can be seen from the report table's column headings).

To fix this kind of thing, be more explicit about the report period (and/or the periodic rules' dates). In this case, adding -b 2020 does the trick.

Selecting budget goals

By default, the budget report uses all available periodic transaction rules to generate goals. This includes rules with a different report interval from your report. Eg if you have daily, weekly and monthly periodic rules, all of these will contribute to the goals in a monthly budget report.

You can select a subset of periodic rules by providing an argument to the --budget flag. --budget=DESCPAT will match all periodic rules whose description contains DESCPAT, a case-insensitive substring (not a regular expression or query). This means you can give your periodic rules descriptions (remember that two spaces are needed between period expression and description), and then select from multiple budgets defined in your journal.

Budgeting vs forecasting

--forecast and --budget both use the periodic transaction rules in the journal to generate temporary transactions for reporting purposes. However they are separate features - though you can use both at the same time if you want. Here are some differences between them:

--forecast--budget
is a general option; it enables forecasting with all 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:

-txtcsvhtmljsonsql
wideYYY
tallYYY
bareYYY
tidyY

Examples:

Wide layout

With many commodities, reports can be very wide:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=wide
Balance changes in 2012-01-01..2014-12-31:

                  ||                                          2012                                                     2013                                             2014                                                      Total 
==================++====================================================================================================================================================================================================================
 Assets:US:ETrade || 10.00 ITOT, 337.18 USD, 12.00 VEA, 106.00 VHT  70.00 GLD, 18.00 ITOT, -98.12 USD, 10.00 VEA, 18.00 VHT  -11.00 ITOT, 4881.44 USD, 14.00 VEA, 170.00 VHT  70.00 GLD, 17.00 ITOT, 5120.50 USD, 36.00 VEA, 294.00 VHT 
------------------++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                  || 10.00 ITOT, 337.18 USD, 12.00 VEA, 106.00 VHT  70.00 GLD, 18.00 ITOT, -98.12 USD, 10.00 VEA, 18.00 VHT  -11.00 ITOT, 4881.44 USD, 14.00 VEA, 170.00 VHT  70.00 GLD, 17.00 ITOT, 5120.50 USD, 36.00 VEA, 294.00 VHT 

A width limit reduces the width, but some commodities will be hidden:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=wide,32
Balance changes in 2012-01-01..2014-12-31:

                  ||                             2012                             2013                   2014                            Total 
==================++===========================================================================================================================
 Assets:US:ETrade || 10.00 ITOT, 337.18 USD, 2 more..  70.00 GLD, 18.00 ITOT, 3 more..  -11.00 ITOT, 3 more..  70.00 GLD, 17.00 ITOT, 3 more.. 
------------------++---------------------------------------------------------------------------------------------------------------------------
                  || 10.00 ITOT, 337.18 USD, 2 more..  70.00 GLD, 18.00 ITOT, 3 more..  -11.00 ITOT, 3 more..  70.00 GLD, 17.00 ITOT, 3 more.. 
Tall layout

Each commodity gets a new line (may be different in each column), and account names are repeated:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=tall
Balance changes in 2012-01-01..2014-12-31:

                  ||       2012        2013         2014        Total 
==================++==================================================
 Assets:US:ETrade || 10.00 ITOT   70.00 GLD  -11.00 ITOT    70.00 GLD 
 Assets:US:ETrade || 337.18 USD  18.00 ITOT  4881.44 USD   17.00 ITOT 
 Assets:US:ETrade ||  12.00 VEA  -98.12 USD    14.00 VEA  5120.50 USD 
 Assets:US:ETrade || 106.00 VHT   10.00 VEA   170.00 VHT    36.00 VEA 
 Assets:US:ETrade ||              18.00 VHT                294.00 VHT 
------------------++--------------------------------------------------
                  || 10.00 ITOT   70.00 GLD  -11.00 ITOT    70.00 GLD 
                  || 337.18 USD  18.00 ITOT  4881.44 USD   17.00 ITOT 
                  ||  12.00 VEA  -98.12 USD    14.00 VEA  5120.50 USD 
                  || 106.00 VHT   10.00 VEA   170.00 VHT    36.00 VEA 
                  ||              18.00 VHT                294.00 VHT 
Bare layout

Commodity symbols are kept in one column, each commodity has its own row, amounts are bare numbers, account names are repeated:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=bare
Balance changes in 2012-01-01..2014-12-31:

                  || Commodity    2012    2013     2014    Total 
==================++=============================================
 Assets:US:ETrade || GLD             0   70.00        0    70.00 
 Assets:US:ETrade || ITOT        10.00   18.00   -11.00    17.00 
 Assets:US:ETrade || USD        337.18  -98.12  4881.44  5120.50 
 Assets:US:ETrade || VEA         12.00   10.00    14.00    36.00 
 Assets:US:ETrade || VHT        106.00   18.00   170.00   294.00 
------------------++---------------------------------------------
                  || GLD             0   70.00        0    70.00 
                  || ITOT        10.00   18.00   -11.00    17.00 
                  || USD        337.18  -98.12  4881.44  5120.50 
                  || VEA         12.00   10.00    14.00    36.00 
                  || VHT        106.00   18.00   170.00   294.00 

Bare layout also affects CSV output, which is useful for producing data that is easier to consume, eg for making charts:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -O csv --layout=bare
"account","commodity","balance"
"Assets:US:ETrade","GLD","70.00"
"Assets:US:ETrade","ITOT","17.00"
"Assets:US:ETrade","USD","5120.50"
"Assets:US:ETrade","VEA","36.00"
"Assets:US:ETrade","VHT","294.00"
"total","GLD","70.00"
"total","ITOT","17.00"
"total","USD","5120.50"
"total","VEA","36.00"
"total","VHT","294.00"

Bare layout will sometimes display an extra row for the no-symbol commodity, because of zero amounts (hledger treats zeroes as commodity-less, usually). This can break hledger-bar confusingly (workaround: add a cur: query to exclude the no-symbol row).

Tidy layout

This produces normalised "tidy data" (see https://cran.r-project.org/web/packages/tidyr/vignettes/tidy-data.html) where every variable has its own column and each row represents a single data point. This is the easiest kind of data for other software to consume:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -Y -O csv --layout=tidy
"account","period","start_date","end_date","commodity","value"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","GLD","0"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","ITOT","10.00"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","USD","337.18"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","VEA","12.00"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","VHT","106.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","GLD","70.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","ITOT","18.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","USD","-98.12"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","VEA","10.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","VHT","18.00"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","GLD","0"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","ITOT","-11.00"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","USD","4881.44"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","VEA","14.00"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","VHT","170.00"

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.

Also:

  • bal -M expenses -2 -SA
    Show monthly expenses summarised to depth 2 and sorted by average amount.

  • bal -M --budget expenses
    Show monthly expenses and budget goals.

  • bal -M --valuechange investments
    Show monthly change in market value of investment assets.

  • bal investments --valuechange -D date:lastweek amt:'>1000' -STA [--invert]
    Show top gainers [or losers] last week

balancesheet

(bs)

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.

Example:

$ hledger balancesheet
Balance Sheet 2008-12-31

                    || 2008-12-31 
====================++============
 Assets             ||            
--------------------++------------
 assets:bank:saving ||         $1 
 assets:cash        ||        $-2 
--------------------++------------
                    ||        $-1 
====================++============
 Liabilities        ||            
--------------------++------------
 liabilities:debts  ||        $-1 
--------------------++------------
                    ||        $-1 
====================++============
 Net:               ||          0 

This command is a higher-level variant of the balance command, and supports many of that command's features, such as multi-period reports. It is similar to hledger balance -H assets liabilities, but with smarter account detection, and liabilities displayed with their sign flipped.

This command also supports the output destination and output format options The output formats supported are txt, csv, tsv (Added in 1.32), html, and json.

balancesheetequity

(bse)

This command displays a balance sheet, showing historical ending balances of asset, liability and equity accounts. Amounts are shown with normal positive sign, as in conventional financial statements.

This report shows accounts declared with the Asset, Cash, Liability or Equity type (see account types). Or if no such accounts are declared, it shows top-level accounts named asset, liability or equity (case insensitive, plurals allowed) and their subaccounts.

Example:

$ hledger balancesheetequity
Balance Sheet With Equity 2008-12-31

                    || 2008-12-31 
====================++============
 Assets             ||            
--------------------++------------
 assets:bank:saving ||         $1 
 assets:cash        ||        $-2 
--------------------++------------
                    ||        $-1 
====================++============
 Liabilities        ||            
--------------------++------------
 liabilities:debts  ||        $-1 
--------------------++------------
                    ||        $-1 
====================++============
 Equity             ||            
--------------------++------------
--------------------++------------
                    ||          0 
====================++============
 Net:               ||          0 

This command is a higher-level variant of the balance command, and supports many of that command's features, such as multi-period reports. It is similar to hledger balance -H assets liabilities equity, but with smarter account detection, and liabilities/equity displayed with their sign flipped.

This report is the easiest way to see if the accounting equation (A+L+E = 0) is satisfied (after you have done a close --retain to merge revenues and expenses with equity, and perhaps added --infer-equity to balance your commodity conversions).

This command also supports the output destination and output format options The output formats supported are txt, csv, tsv, html, and json.

cashflow

(cf)

This command displays a (simple) cashflow statement, showing the inflows and outflows affecting "cash" (ie, liquid, easily convertible) assets. Amounts are shown with normal positive sign, as in conventional financial statements.

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:

^assets?(:.+)?:(cash|bank|che(ck|que?)(ing)?|savings?|currentcash)(:|$)

and their subaccounts.

An example cashflow report:

$ hledger cashflow
Cashflow Statement 2008

                    || 2008 
====================++======
 Cash flows         ||      
--------------------++------
 assets:bank:saving ||   $1 
 assets:cash        ||  $-2 
--------------------++------
                    ||  $-1 

This command is a higher-level variant of the balance command, and supports many of that command's features, such as multi-period reports. It is similar to hledger balance assets not:fixed not:investment not:receivable, but with smarter account detection.

This command also supports the output destination and output format options The output formats supported are txt, csv, tsv (Added in 1.32), html, and json.

check

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

(equity)

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.

codes

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.

Examples:

2022/1/1 (123) Supermarket   
 Food       $5.00
 Checking    

2022/1/2 (124) Post Office
 Postage    $8.32
 Checking

2022/1/3 Supermarket
 Food      $11.23
 Checking 

2022/1/4 (126) Post Office
 Postage    $3.21
 Checking
$ hledger codes
123
124
126
$ hledger codes -E
123
124

126

commodities

List all commodity/currency symbols used or declared in the journal.

demo

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.

Examples:

$ hledger demo               # list available demos
$ hledger demo 1             # play the first demo at default speed (2x)
$ hledger demo install -s4   # play the "install" demo at 4x speed

descriptions

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.

Example:

$ hledger descriptions
Store Name
Gas Station | Petrol
Person A

diff

Compares a particular account's transactions in two input files. It shows any transactions to this account which are in one file but not in the other.

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.

Examples:

$ hledger diff -f $LEDGER_FILE -f bank.csv assets:bank:giro 
These transactions are in the first file only:

2014/01/01 Opening Balances
    assets:bank:giro              EUR ...
    ...
    equity:opening balances       EUR -...

These transactions are in the second file only:

files

List all files included in the journal. With a REGEX argument, only file names matching the regular expression (case sensitive) are shown.

help

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

Examples

$ 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

import

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.

Deduplication

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.

incomestatement

(is)

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.

Example:

$ hledger incomestatement
Income Statement 2008

                   || 2008 
===================++======
 Revenues          ||      
-------------------++------
 income:gifts      ||   $1 
 income:salary     ||   $1 
-------------------++------
                   ||   $2 
===================++======
 Expenses          ||      
-------------------++------
 expenses:food     ||   $1 
 expenses:supplies ||   $1 
-------------------++------
                   ||   $2 
===================++======
 Net:              ||    0 

This command is a higher-level variant of the balance command, and supports many of that command's features, such as multi-period reports. It is similar to hledger balance '(revenues|income)' expenses, but with smarter account detection, and revenues/income displayed with their sign flipped.

This command also supports the output destination and output format options The output formats supported are txt, csv, tsv (Added in 1.32), html, and json.

notes

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

Example:

$ hledger notes
Petrol
Snacks

payees

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.

Example:

$ hledger payees
Store Name
Gas Station
Person A

prices

Print the market prices declared with P directives. With --infer-market-prices, also show any additional prices inferred from costs. With --show-reverse, also show additional prices inferred by reversing known prices.

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.

Eg:

$ hledger print -f examples/sample.journal date:200806
2008/06/01 gift
    assets:bank:checking            $1
    income:gifts                   $-1

2008/06/02 save
    assets:bank:saving              $1
    assets:bank:checking           $-1

2008/06/03 * eat & shop
    expenses:food                $1
    expenses:supplies            $1
    assets:cash                 $-2

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
"txnidx","date","date2","status","code","description","comment","account","amount","commodity","credit","debit","posting-status","posting-comment"
"1","2008/01/01","","","","income","","assets:bank:checking","1","$","","1","",""
"1","2008/01/01","","","","income","","income:salary","-1","$","1","","",""
"2","2008/06/01","","","","gift","","assets:bank:checking","1","$","","1","",""
"2","2008/06/01","","","","gift","","income:gifts","-1","$","1","","",""
"3","2008/06/02","","","","save","","assets:bank:saving","1","$","","1","",""
"3","2008/06/02","","","","save","","assets:bank:checking","-1","$","1","","",""
"4","2008/06/03","","*","","eat & shop","","expenses:food","1","$","","1","",""
"4","2008/06/03","","*","","eat & shop","","expenses:supplies","1","$","","1","",""
"4","2008/06/03","","*","","eat & shop","","assets:cash","-2","$","2","","",""
"5","2008/12/31","","*","","pay off","","liabilities:debts","1","$","","1","",""
"5","2008/12/31","","*","","pay off","","assets:bank:checking","-1","$","1","","",""
  • There is one CSV record per posting, with the parent transaction's fields repeated.
  • The "txnidx" (transaction index) field shows which postings belong to the same transaction. (This number might change if transactions are reordered within the file, files are parsed/included in a different order, etc.)
  • The amount is separated into "commodity" (the symbol) and "amount" (numeric quantity) fields.
  • The numeric amount is repeated in either the "credit" or "debit" column, for convenience. (Those names are not accurate in the accounting sense; it just puts negative amounts under credit and zero or greater amounts under debit.)

register

(reg)

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.

rewrite

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.

Examples:

$ hledger-rewrite.hs ^income --add-posting '(liabilities:tax)  *.33  ; income tax' --add-posting '(reserve:gifts)  $100'
$ hledger-rewrite.hs expenses:gifts --add-posting '(reserve:gifts)  *-1"'
$ hledger-rewrite.hs -f rewrites.hledger

rewrites.hledger may consist of entries like:

= ^income amt:<0 date:2017
  (liabilities:tax)  *0.33  ; tax on income
  (reserve:grocery)  *0.25  ; reserve 25% for grocery
  (reserve:)  *0.25  ; reserve 25% for grocery

Note the single quotes to protect the dollar sign from bash, and the two spaces between account and amount.

More:

$ hledger rewrite -- [QUERY]        --add-posting "ACCT  AMTEXPR" ...
$ hledger rewrite -- ^income        --add-posting '(liabilities:tax)  *.33'
$ hledger rewrite -- expenses:gifts --add-posting '(budget:gifts)  *-1"'
$ hledger rewrite -- ^income        --add-posting '(budget:foreign currency)  *0.25 JPY; diversify'

Argument for --add-posting option is a usual posting of transaction with an exception for amount specification. More precisely, you can use '*' (star symbol) before the amount to indicate that that this is a factor for an amount of original matched posting. If the amount includes a commodity name, the new posting amount will be in the new commodity; otherwise, it will be in the matched posting amount's commodity.

Re-write rules in a file

During the run this tool will execute so called "Automated Transactions" found in any journal it process. I.e instead of specifying this operations in command line you can put them in a journal file.

$ rewrite-rules.journal

Make contents look like this:

= ^income
    (liabilities:tax)  *.33

= expenses:gifts
    budget:gifts  *-1
    assets:budget  *1

Note that '=' (equality symbol) that is used instead of date in transactions you usually write. It indicates the query by which you want to match the posting to add new ones.

$ hledger rewrite -- -f input.journal -f rewrite-rules.journal > rewritten-tidy-output.journal

This is something similar to the commands pipeline:

$ hledger rewrite -- -f input.journal '^income' --add-posting '(liabilities:tax)  *.33' \
  | hledger rewrite -- -f - expenses:gifts      --add-posting 'budget:gifts  *-1'       \
                                                --add-posting 'assets:budget  *1'       \
  > rewritten-tidy-output.journal

It is important to understand that relative order of such entries in journal is important. You can re-use result of previously added postings.

Diff output format

To use this tool for batch modification of your journal files you may find useful output in form of unified diff.

$ hledger rewrite -- --diff -f examples/sample.journal '^income' --add-posting '(liabilities:tax)  *.33'

Output might look like:

--- /tmp/examples/sample.journal
+++ /tmp/examples/sample.journal
@@ -18,3 +18,4 @@
 2008/01/01 income
-    assets:bank:checking  $1
+    assets:bank:checking            $1
     income:salary
+    (liabilities:tax)                0
@@ -22,3 +23,4 @@
 2008/06/01 gift
-    assets:bank:checking  $1
+    assets:bank:checking            $1
     income:gifts
+    (liabilities:tax)                0

If you'll pass this through patch tool you'll get transactions containing the posting that matches your query be updated. Note that multiple files might be update according to list of input files specified via --file options and include directives inside of these files.

Be careful. Whole transaction being re-formatted in a style of output from hledger print.

See also:

https://github.com/simonmichael/hledger/issues/99

rewrite vs. print --auto

This command predates print --auto, and currently does much the same thing, but with these differences:

  • with multiple files, rewrite lets rules in any file affect all other files. print --auto uses standard directive scoping; rules affect only child files.

  • rewrite's query limits which transactions can be rewritten; all are printed. print --auto's query limits which transactions are printed.

  • rewrite applies rules specified on command line or in the journal. print --auto applies rules specified in the journal.

roi

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.

Examples:

Spaces and special characters in --inv and --pnl

Note that --inv and --pnl's argument is a query, and queries could have several space-separated terms (see QUERIES).

To indicate that all search terms form single command-line argument, you will need to put them in quotes (see Special characters):

$ hledger roi --inv 'term1 term2 term3 ...'

If any query terms contain spaces themselves, you will need an extra level of nested quoting, eg:

$ hledger roi --inv="'Assets:Test 1'" --pnl="'Equity:Unrealized Profit and Loss'"

Semantics of --inv and --pnl

Query supplied to --inv has to match all transactions that are related to your investment. Transactions not matching --inv will be ignored.

In these transactions, ROI will conside postings that match --inv to be "investment postings" and other postings (not matching --inv) will be sorted into two categories: "cash flow" and "profit and loss", as ROI needs to know which part of the investment value is your contributions and which is due to the return on investment.

  • "Cash flow" is depositing or withdrawing money, buying or selling assets, or otherwise converting between your investment commodity and any other commodity. Example:

    2019-01-01 Investing in Snake Oil
      assets:cash          -$100
      investment:snake oil
    
    2020-01-01 Selling my Snake Oil
      assets:cash           $10
      investment:snake oil  = 0
    
  • "Profit and loss" is change in the value of your investment:

    2019-06-01 Snake Oil falls in value
      investment:snake oil  = $57
      equity:unrealized profit or loss
    

All non-investment postings are assumed to be "cash flow", unless they match --pnl query. Changes in value of your investment due to "profit and loss" postings will be considered as part of your investment return.

Example: if you use --inv snake --pnl equity:unrealized, then postings in the example below would be classifed as:

2019-01-01 Snake Oil #1
  assets:cash          -$100   ; cash flow posting
  investment:snake oil         ; investment posting

2019-03-01 Snake Oil #2
  equity:unrealized pnl  -$100 ; profit and loss posting
  snake oil                    ; investment posting

2019-07-01 Snake Oil #3
  equity:unrealized pnl        ; profit and loss posting
  cash          -$100          ; cash flow posting
  snake oil     $50            ; investment posting

IRR and TWR explained

"ROI" stands for "return on investment". Traditionally this was computed as a difference between current value of investment and its initial value, expressed in percentage of the initial value.

However, this approach is only practical in simple cases, where investments receives no in-flows or out-flows of money, and where rate of growth is fixed over time. For more complex scenarios you need different ways to compute rate of return, and this command implements two of them: IRR and TWR.

Internal rate of return, or "IRR" (also called "money-weighted rate of return") takes into account effects of in-flows and out-flows, and the time between them. Investment at a particular fixed interest rate is going to give you more interest than the same amount invested at the same interest rate, but made later in time. If you are withdrawing from your investment, your future gains would be smaller (in absolute numbers), and will be a smaller percentage of your initial investment, so your IRR will be smaller. And if you are adding to your investment, you will receive bigger absolute gains, which will be a bigger percentage of your initial investment, so your IRR will be larger.

As mentioned before, in-flows and out-flows would be any cash that you personally put in or withdraw, and for the "roi" command, these are the postings that match the query in the--inv argument and NOT match the query in the--pnl argument.

If you manually record changes in the value of your investment as transactions that balance them against "profit and loss" (or "unrealized gains") account or use price directives, then in order for IRR to compute the precise effect of your in-flows and out-flows on the rate of return, you will need to record the value of your investement on or close to the days when in- or out-flows occur.

In technical terms, IRR uses the same approach as computation of net present value, and tries to find a discount rate that makes net present value of all the cash flows of your investment to add up to zero. This could be hard to wrap your head around, especially if you haven't done discounted cash flow analysis before. Implementation of IRR in hledger should produce results that match the =XIRR formula in Excel.

Second way to compute rate of return that roi command implements is called "time-weighted rate of return" or "TWR". Like IRR, it will account for the effect of your in-flows and out-flows, but unlike IRR it will try to compute the true rate of return of the underlying asset, compensating for the effect that deposits and withdrawas have on the apparent rate of growth of your investment.

TWR represents your investment as an imaginary "unit fund" where in-flows/ out-flows lead to buying or selling "units" of your investment and changes in its value change the value of "investment unit". Change in "unit price" over the reporting period gives you rate of return of your investment, and make TWR less sensitive than IRR to the effects of cash in-flows and out-flows.

References:

stats

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.

Example:

$ hledger stats -f examples/1ktxns-1kaccts.journal 
Main file           : .../1ktxns-1kaccts.journal
Included files      : 0
Txns span           : 2000-01-01 to 2002-09-27 (1000 days)
Last txn            : 2002-09-26 (7827 days ago)
Txns                : 1000 (1.0 per day)
Txns last 30 days   : 0 (0.0 per day)
Txns last 7 days    : 0 (0.0 per day)
Payees/descriptions : 1000
Accounts            : 1000 (depth 10)
Commodities         : 26
Market prices       : 1000
Runtime stats       : 0.12 s elapsed, 8266 txns/s, 4 MB live, 16 MB alloc

This command supports the -o/--output-file option (but not -O/--output-format).

tags

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.

test

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

PART 5: COMMON TASKS

Here are some quick examples of how to do some basic tasks with hledger.

Getting help

Here's how to list commands and view options and command docs:

$ hledger                # show available commands
$ hledger --help         # show common options
$ hledger CMD --help     # show CMD's options, common options and CMD's documentation

You can also view your hledger version's manual in several formats by using the help command. Eg:

$ hledger help           # show the hledger manual with info, man or $PAGER (best available)
$ hledger help journal   # show the journal topic in the hledger manual
$ hledger help --help    # find out more about the help command

To view manuals and introductory docs on the web, visit https://hledger.org. Chat and mail list support and discussion archives can be found at https://hledger.org/support.

Constructing command lines

hledger has a flexible command line interface. We strive to keep it simple and ergonomic, but if you run into one of the sharp edges described in OPTIONS, here are some tips that might help:

  • command-specific options must go after the command (it's fine to put common options there too: hledger CMD OPTS ARGS)
  • 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 hide regular expression metacharacters from the shell
  • to see how a misbehaving command line is being parsed, add --debug=2.

Starting a journal file

hledger looks for your accounting data in a journal file, $HOME/.hledger.journal by default:

$ hledger stats
The hledger journal file "/Users/simon/.hledger.journal" was not found.
Please create it first, eg with "hledger add" or a text editor.
Or, specify an existing journal file with -f or LEDGER_FILE.

You can override this by setting the LEDGER_FILE environment variable (see below). It's a good practice to keep this important file under version control, and to start a new file each year. So you could do something like this:

$ mkdir ~/finance
$ cd ~/finance
$ git init
Initialized empty Git repository in /Users/simon/finance/.git/
$ touch 2023.journal
$ echo "export LEDGER_FILE=$HOME/finance/2023.journal" >> ~/.profile
$ source ~/.profile
$ hledger stats
Main file                : /Users/simon/finance/2023.journal
Included files           : 
Transactions span        :  to  (0 days)
Last transaction         : none
Transactions             : 0 (0.0 per day)
Transactions last 30 days: 0 (0.0 per day)
Transactions last 7 days : 0 (0.0 per day)
Payees/descriptions      : 0
Accounts                 : 0 (depth 0)
Commodities              : 0 ()
Market prices            : 0 ()

Setting LEDGER_FILE

How to set LEDGER_FILE permanently depends on your setup:

On unix and mac, running these commands in the terminal will work for many people; adapt as needed:

$ echo 'export LEDGER_FILE=~/finance/2023.journal' >> ~/.profile
$ source ~/.profile

When correctly configured, in a new terminal window env | grep LEDGER_FILE will show your file, and so will hledger files.

On mac, this additional step might be helpful for GUI applications (like Emacs started from the dock): add an entry to ~/.MacOSX/environment.plist like

{
  "LEDGER_FILE" : "~/finance/2023.journal"
}

and then run killall Dock in a terminal window (or restart the machine).

On Windows, see https://www.java.com/en/download/help/path.html, or try running these commands in a powershell window (let us know if it persists across a reboot, and if you need to be an Administrator):

> CD
> MKDIR finance
> SETX LEDGER_FILE "C:\Users\USERNAME\finance\2023.journal"

Setting opening balances

Pick a starting date for which you can look up the balances of some real-world assets (bank accounts, wallet..) and liabilities (credit cards..).

To avoid a lot of data entry, you may want to start with just one or two accounts, like your checking account or cash wallet; and pick a recent starting date, like today or the start of the week. You can always come back later and add more accounts and older transactions, eg going back to january 1st.

Add an opening balances transaction to the journal, declaring the balances on this date. Here are two ways to do it:

  • The first way: open the journal in any text editor and save an entry like this:

    2023-01-01 * opening balances
        assets:bank:checking                $1000   = $1000
        assets:bank:savings                 $2000   = $2000
        assets:cash                          $100   = $100
        liabilities:creditcard               $-50   = $-50
        equity:opening/closing balances
    

    These are start-of-day balances, ie whatever was in the account at the end of the previous day.

    The * after the date is an optional status flag. Here it means "cleared & confirmed".

    The currency symbols are optional, but usually a good idea as you'll be dealing with multiple currencies sooner or later.

    The = amounts are optional balance assertions, providing extra error checking.

  • The second way: run hledger add and follow the prompts to record a similar transaction:

    $ hledger add
    Adding transactions to journal file /Users/simon/finance/2023.journal
    Any command line arguments will be used as defaults.
    Use tab key to complete, readline keys to edit, enter to accept defaults.
    An optional (CODE) may follow transaction dates.
    An optional ; COMMENT may follow descriptions or amounts.
    If you make a mistake, enter < at any prompt to go one step backward.
    To end a transaction, enter . when prompted.
    To quit, enter . at a date prompt or press control-d or control-c.
    Date [2023-02-07]: 2023-01-01
    Description: * opening balances
    Account 1: assets:bank:checking
    Amount  1: $1000
    Account 2: assets:bank:savings
    Amount  2 [$-1000]: $2000
    Account 3: assets:cash
    Amount  3 [$-3000]: $100
    Account 4: liabilities:creditcard
    Amount  4 [$-3100]: $-50
    Account 5: equity:opening/closing balances
    Amount  5 [$-3050]: 
    Account 6 (or . or enter to finish this transaction): .
    2023-01-01 * opening balances
        assets:bank:checking                      $1000
        assets:bank:savings                       $2000
        assets:cash                                $100
        liabilities:creditcard                     $-50
        equity:opening/closing balances          $-3050
    
    Save this transaction to the journal ? [y]: 
    Saved.
    Starting the next transaction (. or ctrl-D/ctrl-C to quit)
    Date [2023-01-01]: .
    

If you're using version control, this could be a good time to commit the journal. Eg:

$ git commit -m 'initial balances' 2023.journal

Recording transactions

As you spend or receive money, you can record these transactions using one of the methods above (text editor, hledger add) or by using the hledger-iadd or hledger-web add-ons, or by using the import command to convert CSV data downloaded from your bank.

Here are some simple transactions, see the hledger_journal(5) manual and hledger.org for more ideas:

2023/1/10 * gift received
  assets:cash   $20
  income:gifts

2023.1.12 * farmers market
  expenses:food    $13
  assets:cash

2023-01-15 paycheck
  income:salary
  assets:bank:checking    $1000

Reconciling

Periodically you should reconcile - compare your hledger-reported balances against external sources of truth, like bank statements or your bank's website - to be sure that your ledger accurately represents the real-world balances (and, that the real-world institutions have not made a mistake!). This gets easy and fast with (1) practice and (2) frequency. If you do it daily, it can take 2-10 minutes. If you let it pile up, expect it to take longer as you hunt down errors and discrepancies.

A typical workflow:

  1. Reconcile cash. Count what's in your wallet. Compare with what hledger reports (hledger bal cash). If they are different, try to remember the missing transaction, or look for the error in the already-recorded transactions. A register report can be helpful (hledger reg cash). If you can't find the error, add an adjustment transaction. Eg if you have $105 after the above, and can't explain the missing $2, it could be:

    2023-01-16 * adjust cash
        assets:cash    $-2 = $105
        expenses:misc
    
  2. Reconcile checking. Log in to your bank's website. Compare today's (cleared) balance with hledger's cleared balance (hledger bal checking -C). If they are different, track down the error or record the missing transaction(s) or add an adjustment transaction, similar to the above. Unlike the cash case, you can usually compare the transaction history and running balance from your bank with the one reported by hledger reg checking -C. This will be easier if you generally record transaction dates quite similar to your bank's clearing dates.

  3. Repeat for other asset/liability accounts.

Tip: instead of the register command, use hledger-ui to see a live-updating register while you edit the journal: hledger-ui --watch --register checking -C

After reconciling, it could be a good time to mark the reconciled transactions' status as "cleared and confirmed", if you want to track that, by adding the * marker. Eg in the paycheck transaction above, insert * between 2023-01-15 and paycheck

If you're using version control, this can be another good time to commit:

$ git commit -m 'txns' 2023.journal

Reporting

Here are some basic reports.

Show all transactions:

$ hledger print
2023-01-01 * opening balances
    assets:bank:checking                      $1000
    assets:bank:savings                       $2000
    assets:cash                                $100
    liabilities:creditcard                     $-50
    equity:opening/closing balances          $-3050

2023-01-10 * gift received
    assets:cash              $20
    income:gifts

2023-01-12 * farmers market
    expenses:food             $13
    assets:cash

2023-01-15 * paycheck
    income:salary
    assets:bank:checking           $1000

2023-01-16 * adjust cash
    assets:cash               $-2 = $105
    expenses:misc

Show account names, and their hierarchy:

$ hledger accounts --tree
assets
  bank
    checking
    savings
  cash
equity
  opening/closing balances
expenses
  food
  misc
income
  gifts
  salary
liabilities
  creditcard

Show all account totals:

$ hledger balance
               $4105  assets
               $4000    bank
               $2000      checking
               $2000      savings
                $105    cash
              $-3050  equity:opening/closing balances
                 $15  expenses
                 $13    food
                  $2    misc
              $-1020  income
                $-20    gifts
              $-1000    salary
                $-50  liabilities:creditcard
--------------------
                   0

Show only asset and liability balances, as a flat list, limited to depth 2:

$ hledger bal assets liabilities -2
               $4000  assets:bank
                $105  assets:cash
                $-50  liabilities:creditcard
--------------------
               $4055

Show the same thing without negative numbers, formatted as a simple balance sheet:

$ hledger bs -2
Balance Sheet 2023-01-16

                        || 2023-01-16 
========================++============
 Assets                 ||            
------------------------++------------
 assets:bank            ||      $4000 
 assets:cash            ||       $105 
------------------------++------------
                        ||      $4105 
========================++============
 Liabilities            ||            
------------------------++------------
 liabilities:creditcard ||        $50 
------------------------++------------
                        ||        $50 
========================++============
 Net:                   ||      $4055 

The final total is your "net worth" on the end date. (Or use bse for a full balance sheet with equity.)

Show income and expense totals, formatted as an income statement:

hledger is 
Income Statement 2023-01-01-2023-01-16

               || 2023-01-01-2023-01-16 
===============++=======================
 Revenues      ||                       
---------------++-----------------------
 income:gifts  ||                   $20 
 income:salary ||                 $1000 
---------------++-----------------------
               ||                 $1020 
===============++=======================
 Expenses      ||                       
---------------++-----------------------
 expenses:food ||                   $13 
 expenses:misc ||                    $2 
---------------++-----------------------
               ||                   $15 
===============++=======================
 Net:          ||                 $1005 

The final total is your net income during this period.

Show transactions affecting your wallet, with running total:

$ hledger register cash
2023-01-01 opening balances     assets:cash                   $100          $100
2023-01-10 gift received        assets:cash                    $20          $120
2023-01-12 farmers market       assets:cash                   $-13          $107
2023-01-16 adjust cash          assets:cash                    $-2          $105

Show weekly posting counts as a bar chart:

$ hledger activity -W
2019-12-30 *****
2023-01-06 ****
2023-01-13 ****

Migrating to a new file

At the end of the year, you may want to continue your journal in a new file, so that old transactions don't slow down or clutter your reports, and to help ensure the integrity of your accounting history. See the close command.

If using version control, don't forget to git add the new file.

BUGS

We welcome bug reports in the hledger issue tracker (shortcut: http://bugs.hledger.org), or on the #hledger chat or hledger mail list (https://hledger.org/support).

Some known issues and limitations:

The need to precede add-on command options with -- when invoked from hledger is awkward. (See Command options, Constructing command lines.)

A UTF-8-aware system locale must be configured to work with non-ascii data. (See Unicode characters, Troubleshooting.)

On Microsoft Windows, depending whether you are running in a CMD window or a Cygwin/MSYS/Mintty window and how you installed hledger, non-ascii characters and colours may not be supported, and the tab key may not be supported by hledger add. (Running in a WSL window should resolve these.)

When processing large data files, hledger uses more memory than Ledger.

Troubleshooting

Here are some common issues you might encounter when you run hledger, and how to resolve them (and remember also you can usually get quick Support):

PATH issues: I get an error like "No command 'hledger' found"
Depending how you installed hledger, the executables may not be in your shell's PATH. Eg on unix systems, stack installs hledger in ~/.local/bin and cabal installs it in ~/.cabal/bin. You may need to add one of these directories to your shell's PATH, and/or open a new terminal window.

LEDGER_FILE issues: I configured LEDGER_FILE but hledger is not using it

  • LEDGER_FILE should be a real environment variable, not just a shell variable. Eg on unix, the command env | grep LEDGER_FILE should show it. You may need to use export (see https://stackoverflow.com/a/7411509).
  • You may need to force your shell to see the new configuration. A simple way is to close your terminal window and open a new one.

LANG issues: I get errors like "Illegal byte sequence" or "Invalid or incomplete multibyte or wide character" or "commitAndReleaseBuffer: invalid argument (invalid character)"
Programs compiled with GHC (hledger, haskell build tools, etc.) need the system locale to be UTF-8-aware, or they will fail when they encounter non-ascii characters. To fix it, set the LANG environment variable to a locale which supports UTF-8 and which is installed on your system.

On unix, locale -a lists the installed locales. Look for one which mentions utf8, UTF-8 or similar. Some examples: C.UTF-8, en_US.utf-8, fr_FR.utf8. If necessary, use your system package manager to install one. Then select it by setting the LANG environment variable. Note, exact spelling and capitalisation of the locale name may be important: Here's one common way to configure this permanently for your shell:

$ echo "export LANG=en_US.utf8" >>~/.profile
# close and re-open terminal window

If you are using Nix (not NixOS) for GHC and Hledger, you might need to set the LOCALE_ARCHIVE variable:

$ echo "export LOCALE_ARCHIVE=${glibcLocales}/lib/locale/locale-archive" >>~/.profile
# close and re-open terminal window

COMPATIBILITY ISSUES: hledger gives an error with my Ledger file
Not all of Ledger's journal file syntax or feature set is supported. See hledger and Ledger for full details.

hledger-ui

NAME

hledger-ui - robust, friendly plain text accounting (TUI version)

SYNOPSIS

hledger-ui [OPTS] [QUERYARGS]
hledger ui -- [OPTS] [QUERYARGS]

DESCRIPTION

This manual is for hledger's terminal interface, version 1.33. See also the hledger manual for common concepts and file formats.

hledger is a robust, user-friendly, cross-platform set of programs for tracking money, time, or any other commodity, using double-entry accounting and a simple, editable file format. hledger is inspired by and largely compatible with ledger(1), and largely interconvertible with beancount(1).

hledger-ui is hledger's terminal interface, providing an efficient full-window text UI for viewing accounts and transactions, and some limited data entry capability. It is easier than hledger's command-line interface, and sometimes quicker and more convenient than the web interface.

Like hledger, it reads from (and appends to) a journal file specified by the LEDGER_FILE environment variable (defaulting to $HOME/.hledger.journal); or you can specify files with -f options. It can also read timeclock files, timedot files, or any CSV/SSV/TSV file with a date field. (See hledger(1) -> Input for details.)

Unlike hledger, hledger-ui hides all future-dated transactions by default. They can be revealed, along with any rule-generated periodic transactions, by pressing the F key (or starting with --forecast) to enable "forecast mode".

OPTIONS

Any QUERYARGS are interpreted as a hledger search query which filters the data.

hledger-ui provides the following options:

-w --watch : watch for data and date changes and reload automatically

--theme=default|terminal|greenterm|dark : use this custom display theme

--menu : start in the menu screen

--cash : start in the cash accounts screen

--bs : start in the balance sheet accounts screen

--is : start in the income statement accounts screen

--all : start in the all accounts screen

--register=ACCTREGEX : start in the (first) matched account's register screen

--change : show period balances (changes) at startup instead of historical balances

-l --flat : show accounts as a flat list (default)

-t --tree : show accounts as a tree

hledger-ui also supports many of hledger's general options (and the hledger manual's command line tips also apply here):

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.

MOUSE

In most modern terminals, you can navigate through the screens with a mouse or touchpad:

  • Use mouse wheel or trackpad to scroll up and down
  • Click on list items to go deeper
  • Click on the left margin (column 0) to go back.

KEYS

Keyboard gives more control.

? shows a help dialog listing all keys. (Some of these also appear in the quick help at the bottom of each screen.) Press ? again (or ESCAPE, or LEFT, or q) to close it. The following keys work on most screens:

The cursor keys navigate: RIGHT or ENTER goes deeper, LEFT returns to the previous screen, UP/DOWN/PGUP/PGDN/HOME/END move up and down through lists. Emacs-style (CTRL-p/CTRL-n/CTRL-f/CTRL-b) and VI-style (k,j,l,h) movement keys are also supported. A tip: movement speed is limited by your keyboard repeat rate, to move faster you may want to adjust it. (If you're on a mac, the karabiner app is one way to do that.)

With shift pressed, the cursor keys adjust the report period, limiting the transactions to be shown (by default, all are shown). SHIFT-DOWN/UP steps downward and upward through these standard report period durations: year, quarter, month, week, day. Then, SHIFT-LEFT/RIGHT moves to the previous/next period. T sets the report period to today. With the -w/--watch option, when viewing a "current" period (the current day, week, month, quarter, or year), the period will move automatically to track the current date. To set a non-standard period, you can use / and a date: query.

(Mac users: SHIFT-DOWN/UP keys do not work by default in Terminal, as of MacOS Monterey. You can configure them as follows: open Terminal, press CMD-comma to open preferences, click Profiles, select your current terminal profile on the left, click Keyboard on the right, click + and add this for Shift-Down: \033[1;2B, click + and add this for Shift-Up: \033[1;2A. Press the Escape key to enter the \033 part, you can't type it directly.)

/ lets you set a general filter query limiting the data shown, using the same query terms as in hledger and hledger-web. While editing the query, you can use CTRL-a/e/d/k, BS, cursor keys; press ENTER to set it, or ESCAPEto cancel. There are also keys for quickly adjusting some common filters like account depth and transaction status (see below). BACKSPACE or DELETE removes all filters, showing all transactions.

As mentioned above, by default hledger-ui hides future transactions - both ordinary transactions recorded in the journal, and periodic transactions generated by rule. F toggles forecast mode, in which future/forecasted transactions are shown.

ESCAPE resets the UI state and jumps back to the top screen, restoring the app's initial state at startup. Or, it cancels minibuffer data entry or the help dialog.

CTRL-l redraws the screen and centers the selection if possible (selections near the top won't be centered, since we don't scroll above the top).

g reloads from the data file(s) and updates the current screen and any previous screens. (With large files, this could cause a noticeable pause.)

I toggles balance assertion checking. Disabling balance assertions temporarily can be useful for troubleshooting.

a runs command-line hledger's add command, and reloads the updated file. This allows some basic data entry.

A is like a, but runs the hledger-iadd tool, which provides a terminal interface. This key will be available if hledger-iadd is installed in $path.

E runs $HLEDGER_UI_EDITOR, or $EDITOR, or a default (emacsclient -a "" -nw) on the journal file. With some editors (emacs, vi), the cursor will be positioned at the current transaction when invoked from the register and transaction screens, and at the error location (if possible) when invoked from the error screen.

B toggles cost mode, showing amounts converted to their cost's commodity (see hledger manual > Cost reporting.

V toggles value mode, showing amounts converted to their market value (see hledger manual > Valuation flag). More specifically,

  1. By default, the V key toggles showing end value (--value=end) on or off. The valuation date will be the report end date if specified, otherwise today.

  2. If you started hledger-ui with some other valuation (such as --value=then,EUR), the V key toggles that off or on.

Cost/value tips: - When showing end value, you can change the report end date without restarting, by pressing / and adding a query like date:..YYYY-MM-DD. - Either cost mode, or value mode, can be active, but not both at once. Cost mode takes precedence. - There's not yet any visual indicator that cost or value mode is active, other than the amount values.

q quits the application.

Additional screen-specific keys are described below.

SCREENS

At startup, hledger-ui shows a menu screen by default. From here you can navigate to other screens using the cursor keys: UP/DOWN to select, RIGHT to move to the selected screen, LEFT to return to the previous screen. Or you can use ESC to return directly to the top menu screen.

You can also use a command line flag to specific a different startup screen (--cs, --bs, --is, --all, or --register=ACCT).

This is the top-most screen. From here you can navigate to several screens listing accounts of various types. Note some of these may not show anything until you have configured account types.

Cash accounts

This screen shows "cash" (ie, liquid asset) accounts (like hledger balancesheet type:c). It always shows balances (historical ending balances on the date shown in the title line).

Balance sheet accounts

This screen shows asset, liability and equity accounts (like hledger balancesheetequity). It always shows balances.

Income statement accounts

This screen shows revenue and expense accounts (like hledger incomestatement). It always shows changes (balance changes in the period shown in the title line).

All accounts

This screen shows all accounts in your journal (unless filtered by a query; like hledger balance). It shows balances by default; you can toggle showing changes with the H key.

Register

This screen shows the transactions affecting a particular account. Each line represents one transaction, and shows:

  • the other account(s) involved, in abbreviated form. (If there are both real and virtual postings, it shows only the accounts affected by real postings.)

  • the overall change to the current account's balance; positive for an inflow to this account, negative for an outflow.

  • the running total after the transaction. With the H key you can toggle between

    • the period total, which is from just the transactions displayed
    • or the historical total, which includes any undisplayed transactions before the start of the report period (and matching the filter query if any). This will be the running historical balance (what you would see on a bank's website, eg) if not disturbed by a query.

Note, this screen combines each transaction's in-period postings to a single line item, dated with the earliest in-period transaction or posting date (like hledger's aregister). So custom posting dates can cause the running balance to be temporarily inaccurate. (See hledger manual > aregister and posting dates.)

Transactions affecting this account's subaccounts will be included in the register if the accounts screen is in tree mode, or if it's in list mode but this account has subaccounts which are not shown due to a depth limit. In other words, the register always shows the transactions contributing to the balance shown on the accounts screen. Tree mode/list mode can be toggled with t here also.

U toggles filtering by unmarked status, showing or hiding unmarked transactions. Similarly, P toggles pending transactions, and C toggles cleared transactions. (By default, transactions with all statuses are shown; if you activate one or two status filters, only those transactions are shown; and if you activate all three, the filter is removed.)

R toggles real mode, in which virtual postings are ignored.

z toggles nonzero mode, in which only transactions posting a nonzero change are shown (hledger-ui shows zero items by default, unlike command-line hledger).

Press RIGHT to view the selected transaction in detail.

Transaction

This screen shows a single transaction, as a general journal entry, similar to hledger's print command and journal format (hledger_journal(5)).

The transaction's date(s) and any cleared flag, transaction code, description, comments, along with all of its account postings are shown. Simple transactions have two postings, but there can be more (or in certain cases, fewer).

UP and DOWN will step through all transactions listed in the previous account register screen. In the title bar, the numbers in parentheses show your position within that account register. They will vary depending on which account register you came from (remember most transactions appear in multiple account registers). The #N number preceding them is the transaction's position within the complete unfiltered journal, which is a more stable id (at least until the next reload).

On this screen (and the register screen), the E key will open your text editor with the cursor positioned at the current transaction if possible.

This screen has a limitation with showing file updates: it will not show them until you exit and re-enter it. So eg to see the effect of using the E key, currently you must: - press E, edit and save the file, then exit the editor, returning to hledger-ui - press g to reload the file (or use -w/--watch mode) - press LEFT then RIGHT to exit and re-enter the transaction screen.

Error

This screen will appear if there is a problem, such as a parse error, when you press g to reload. Once you have fixed the problem, press g again to reload and resume normal operation. (Or, you can press escape to cancel the reload attempt.)

TIPS

Watch mode

One of hledger-ui's best features is the auto-reloading -w/--watch mode. With this flag, it will update the display automatically whenever changes are saved to the data files.

This is very useful when reconciling. A good workflow is to have your bank's online register open in a browser window, for reference; the journal file open in an editor window; and hledger-ui in watch mode in a terminal window, eg:

$ hledger-ui --watch --register checking -C

As you mark things cleared in the editor, you can see the effect immediately without having to context switch. This leaves more mental bandwidth for your accounting. Of course you can still interact with hledger-ui when needed, eg to toggle cleared mode, or to explore the history.

There are currently some limitations with --watch:

It may not work correctly for you, depending on platform or system configuration. (Eg #836.)

At least on mac, there can be a slow build-up of CPU usage over time, until the program is restarted (or, suspending and restarting with CTRL-z fg may be enough).

It will not detect file changes made by certain editors, such as Jetbrains IDEs or gedit, or on certain less common filesystems. (To work around, press g to reload manually, or try #1617's fs.inotify.max_user_watches workaround and let us know.)

If you are viewing files mounted from another machine, the system clocks on both machines should be roughly in agreement.

Debug output

You can add --debug[=N] to the command line to log debug output. This will be logged to the file hledger-ui.log in the current directory. N ranges from 1 (least output, the default) to 9 (maximum output).

ENVIRONMENT

COLUMNS The screen width to use. Default: the full terminal width.

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

BUGS

We welcome bug reports in the hledger issue tracker (shortcut: http://bugs.hledger.org), or on the #hledger chat or hledger mail list (https://hledger.org/support).

Some known issues:

-f- doesn't work (hledger-ui can't read from stdin).

If you press g with large files, there could be a noticeable pause.

The Transaction screen does not update from file changes until you exit and re-endter it (see SCREENS > Transaction above).

--watch is not yet fully robust on all platforms (see Watch mode above).

hledger-web

NAME

hledger-web - robust, friendly plain text accounting (Web version)

SYNOPSIS

hledger-web [--serve|--serve-api] [OPTS] [ARGS]
hledger web -- [--serve|--serve-api] [OPTS] [ARGS]

DESCRIPTION

This manual is for hledger's web interface, version 1.33. See also the hledger manual for common concepts and file formats.

hledger is a robust, user-friendly, cross-platform set of programs for tracking money, time, or any other commodity, using double-entry accounting and a simple, editable file format. hledger is inspired by and largely compatible with ledger(1), and largely interconvertible with beancount(1).

hledger-web is a simple web application for browsing and adding transactions. It provides a more user-friendly UI than the hledger CLI or hledger-ui TUI, showing more at once (accounts, the current account register, balance charts) and allowing history-aware data entry, interactive searching, and bookmarking.

hledger-web also lets you share a journal with multiple users, or even the public web. There is no access control, so if you need that you should put it behind a suitable web proxy. As a small protection against data loss when running an unprotected instance, it writes a numbered backup of the main journal file (only) on every edit.

Like hledger, it reads from (and appends to) a journal file specified by the LEDGER_FILE environment variable (defaulting to $HOME/.hledger.journal); or you can specify files with -f options. It can also read timeclock files, timedot files, or any CSV/SSV/TSV file with a date field. (See hledger(1) -> Input for details.)

hledger-web can be run in three modes:

  • Transient mode (the default): your default web browser will be opened to show the app if possible, and the app exits automatically after two minutes of inactivity (no requests received and no open browser windows viewing it).

  • With --serve: the app runs without stopping, and without opening a browser.

  • With --serve-api: only the JSON API is served.

In all cases hledger-web runs as a foreground process, logging requests to stdout.

OPTIONS

hledger-web provides the following options:

--serve : serve and log requests, don't browse or auto-exit after timeout

--serve-api : like --serve, but serve only the JSON web API, not the web UI

--allow=view|add|edit : set the user's access level for changing data (default: add). It also accepts sandstorm for use on that platform (reads permissions from the X-Sandstorm-Permissions request header).

--cors=ORIGIN : allow cross-origin requests from the specified origin; setting ORIGIN to "*" allows requests from any origin

--host=IPADDR : listen on this IP address (default: 127.0.0.1)

By default the server listens on IP address 127.0.0.1, which is accessible only to requests from the local machine.. You can use --host to listen on a different address configured on the machine, eg to allow access from other machines. The special address 0.0.0.0 causes it to listen on all addresses configured on the machine.

--port=PORT : listen on this TCP port (default: 5000)

Similarly, you can use --port to listen on a TCP port other than 5000. This is useful if you want to run multiple hledger-web instances on a machine.

--socket=SOCKETFILE : listen on the given unix socket instead of an IP address and port (unix only; implies --serve)

When --socket is used, hledger-web creates and communicates via a socket file instead of a TCP port. This can be more secure, respects unix file permissions, and makes certain use cases easier, such as running per-user instances behind an nginx reverse proxy. (Eg: proxy_pass http://unix:/tmp/hledger/${remote_user}.socket;.)

--base-url=URL : set the base url (default: http://IPADDR:PORT).

You can use --base-url to change the protocol, hostname, port and path that appear in hledger-web's hyperlinks. This is useful eg when integrating hledger-web within a larger website. The default is http://HOST:PORT/ using the server's configured host address and TCP port (or http://HOST if PORT is 80). Note this affects url generation but not route parsing.

--test : run hledger-web's tests and exit. hspec test runner args may follow a --, eg: hledger-web --test -- --help

hledger-web also supports many of hledger's general options. Query options and arguments may be used to set an initial filter, which although not shown in the UI, will restrict the data shown, in addition to any search query entered in the UI.

Note that hledger-web shows accounts with zero balances by default, like hledger-ui (and unlike hledger). Using the -E/--empty flag at startup will hide them.

If you see accounts which appear to have a zero balance, but cannot be hidden with -E: these have a mixed-cost balance which looks like zero when costs are hidden. Currently hledger-web does not show costs at all.

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.

PERMISSIONS

By default, hledger-web allows anyone who can reach it to view the journal and to add new transactions, but not to change existing data.

You can restrict who can reach it by

  • setting the IP address it listens on (see --host above). By default it listens on 127.0.0.1, accessible to all users on the local machine.
  • putting it behind an authenticating proxy, using eg apache or nginx
  • custom firewall rules

You can restrict what the users who reach it can do, by

  • using the --capabilities=CAP[,CAP..] flag when you start it, enabling one or more of the following capabilities. The default value is view,add:
    • view - allows viewing the journal file and all included files
    • add - allows adding new transactions to the main journal file
    • manage - allows editing, uploading or downloading the main or included files
  • using the --capabilities-header=HTTPHEADER flag to specify a HTTP header from which it will read capabilities to enable. hledger-web on Sandstorm uses the X-Sandstorm-Permissions header to integrate with Sandstorm's permissions. This is disabled by default.

EDITING, UPLOADING, DOWNLOADING

If you enable the manage capability mentioned above, you'll see a new "spanner" button to the right of the search form. Clicking this will let you edit, upload, or download the journal file or any files it includes.

Note, unlike any other hledger command, in this mode you (or any visitor) can alter or wipe the data files.

Normally whenever a file is changed in this way, hledger-web saves a numbered backup (assuming file permissions allow it, the disk is not full, etc.) hledger-web is not aware of version control systems, currently; if you use one, you'll have to arrange to commit the changes yourself (eg with a cron job or a file watcher like entr).

Changes which would leave the journal file(s) unparseable or non-valid (eg with failing balance assertions) are prevented. (Probably. This needs re-testing.)

RELOADING

hledger-web detects changes made to the files by other means (eg if you edit it directly, outside of hledger-web), and it will show the new data when you reload the page or navigate to a new page. If a change makes a file unparseable, hledger-web will display an error message until the file has been fixed.

(Note: if you are viewing files mounted from another machine, make sure that both machine clocks are roughly in step.)

JSON API

In addition to the web UI, hledger-web also serves a JSON API that can be used to get data or add new transactions. If you want the JSON API only, you can use the --serve-api flag. Eg:

$ hledger-web -f examples/sample.journal --serve-api
...

You can get JSON data from these routes:

/version
/accountnames
/transactions
/prices
/commodities
/accounts
/accounttransactions/ACCOUNTNAME

Eg, all account names in the journal (similar to the accounts command). (hledger-web's JSON does not include newlines, here we use python to prettify it):

$ curl -s http://127.0.0.1:5000/accountnames | python -m json.tool
[
    "assets",
    "assets:bank",
    "assets:bank:checking",
    "assets:bank:saving",
    "assets:cash",
    "expenses",
    "expenses:food",
    "expenses:supplies",
    "income",
    "income:gifts",
    "income:salary",
    "liabilities",
    "liabilities:debts"
]

Or all transactions:

$ curl -s http://127.0.0.1:5000/transactions | python -m json.tool
[
    {
        "tcode": "",
        "tcomment": "",
        "tdate": "2008-01-01",
        "tdate2": null,
        "tdescription": "income",
        "tindex": 1,
        "tpostings": [
            {
                "paccount": "assets:bank:checking",
                "pamount": [
                    {
                        "acommodity": "$",
                        "aismultiplier": false,
                        "aprice": null,
...

Most of the JSON corresponds to hledger's data types; for details of what the fields mean, see the Hledger.Data.Json haddock docs and click on the various data types, eg Transaction. And for a higher level understanding, see the journal docs.

In some cases there is outer JSON corresponding to a "Report" type. To understand that, go to the Hledger.Web.Handler.MiscR haddock and look at the source for the appropriate handler to see what it returns. Eg for /accounttransactions it's getAccounttransactionsR, returning a "accountTransactionsReport ...". Looking up the haddock for that we can see that /accounttransactions returns an AccountTransactionsReport, which consists of a report title and a list of AccountTransactionsReportItem (etc).

You can add a new transaction to the journal with a PUT request to /add, if hledger-web was started with the add capability (enabled by default). The payload must be the full, exact JSON representation of a hledger transaction (partial data won't do). You can get sample JSON from hledger-web's /transactions or /accounttransactions, or you can export it with hledger-lib, eg like so:

.../hledger$ stack ghci hledger-lib
>>> writeJsonFile "txn.json" (head $ jtxns samplejournal)
>>> :q

Here's how it looks as of hledger-1.17 (remember, this JSON corresponds to hledger's Transaction and related data types):

{
    "tcomment": "",
    "tpostings": [
        {
            "pbalanceassertion": null,
            "pstatus": "Unmarked",
            "pamount": [
                {
                    "aprice": null,
                    "acommodity": "$",
                    "aquantity": {
                        "floatingPoint": 1,
                        "decimalPlaces": 10,
                        "decimalMantissa": 10000000000
                    },
                    "aismultiplier": false,
                    "astyle": {
                        "ascommodityside": "L",
                        "asdigitgroups": null,
                        "ascommodityspaced": false,
                        "asprecision": 2,
                        "asdecimalpoint": "."
                    }
                }
            ],
            "ptransaction_": "1",
            "paccount": "assets:bank:checking",
            "pdate": null,
            "ptype": "RegularPosting",
            "pcomment": "",
            "pdate2": null,
            "ptags": [],
            "poriginal": null
        },
        {
            "pbalanceassertion": null,
            "pstatus": "Unmarked",
            "pamount": [
                {
                    "aprice": null,
                    "acommodity": "$",
                    "aquantity": {
                        "floatingPoint": -1,
                        "decimalPlaces": 10,
                        "decimalMantissa": -10000000000
                    },
                    "aismultiplier": false,
                    "astyle": {
                        "ascommodityside": "L",
                        "asdigitgroups": null,
                        "ascommodityspaced": false,
                        "asprecision": 2,
                        "asdecimalpoint": "."
                    }
                }
            ],
            "ptransaction_": "1",
            "paccount": "income:salary",
            "pdate": null,
            "ptype": "RegularPosting",
            "pcomment": "",
            "pdate2": null,
            "ptags": [],
            "poriginal": null
        }
    ],
    "ttags": [],
    "tsourcepos": {
        "tag": "JournalSourcePos",
        "contents": [
            "",
            [
                1,
                1
            ]
        ]
    },
    "tdate": "2008-01-01",
    "tcode": "",
    "tindex": 1,
    "tprecedingcomment": "",
    "tdate2": null,
    "tdescription": "income",
    "tstatus": "Unmarked"
}

And here's how to test adding it with curl. This should add a new entry to your journal:

$ curl http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @txn.json

DEBUG OUTPUT

Debug output

You can add --debug[=N] to the command line to log debug output. N ranges from 1 (least output, the default) to 9 (maximum output). Typically you would start with 1 and increase until you are seeing enough. Debug output goes to stderr, interleaved with the requests logged on stdout. To capture debug output in a log file instead, you can usually redirect stderr, eg:
hledger-web --debug=3 2>hledger-web.log.

ENVIRONMENT

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

BUGS

We welcome bug reports in the hledger issue tracker (shortcut: http://bugs.hledger.org), or on the #hledger chat or hledger mail list (https://hledger.org/support).

Some known issues:

Does not work well on small screens, or in text-mode browsers.

Scripts and add-ons

This document is the README in the hledger repo's bin directory, and is also published as Scripts and add-ons on hledger.org.

Add-on commands are executable script files or compiled programs named hledger-*, which show up in hledger's commands list. Some notable add-ons are listed in the hledger manual.

The rest of this page lists smaller scripts and add-ons which are collected in bin/, grouped by how closely they work with hledger:

To be clear: you don't need any of these when starting out with hledger. hledger comes with many built-in commands, and you may want to get familiar with those first.

These scripts don't use hledger directly, but are complementary and might be useful to hledger users. (plaintextaccounting.org has a longer list of PTA tools.)

paypaljson

paypaljson downloads the last 30 days of Paypal transactions (requires a free developer account & API key).

paypaljson2csv

paypaljson2csv (python) converts paypaljson's output to CSV, with format similar to Paypal's manually-downloaded CSV.

HLEDGER-RUNNING

These scripts run hledger via its CLI, eg to help you produce a particular report without needing to remember a complicated command line. They might also consume its text or CSV or JSON output. They can be small shell aliases or functions (typically defined in shell startup files like ~/.bashrc) or individual script files written in shell or another language (typically kept in ~/bin/ or elsewhere in $PATH).

bashrc

bashrc contains many example bash aliases and functions. After installing the bin scripts: as a bash user,

# customise FINDIR and LEDGER_FILE at the top of bin/bashrc
$ . bin/bashrc
$ fin        # list the scripts available

ft

ft is a way to organise your finance-related reports and scripts using standard bash. (See also justfile below.)

$ alias f=~/src/hledger/bin/ft
$ f
--------------------------------------------------------------------------------"; }
ft - finance tool: run financial reports and finance-related scripts
Usage: ft [COMMAND [ARGS]]
Commands:
help           show this help
get-csv        download auto-downloadable CSVs (paypal)
import-dry     import new downloaded transactions to the journal, dry run
import         import new downloaded transactions to the journal, logging and not printing errors
get-prices     [PRICEHISTFETCHOPTS] - download prices for main commodities (default: today's)
bs             show balance sheet
is             show income statement
a              show assets
r              show revenues
x              show expenses
ab             show assets bar chart
rb             show revenues bar chart
xb             show expenses bar chart
al             show assets line chart
rl             show revenues line chart
xl             show expenses line chart
forecast       print transactions predicted by forecast rules from last week on
household      show a draft month-end household adjustment transaction for last month
consulting     show consulting revenue
bin            [PAT]  show all scripts in $DIR/bin/[bashrc] (default: ~/finance/)
OTHERCMD               [ARGS] run other hledger commands on the default journal

Add hledger options to customise reports.

tt

tt is a similar bash multi-script for time reports.

$ alias t=~/src/hledger/bin/tt
$ t
--------------------------------------------------------------------------------
tt - time tool: run time reports and time-related scripts
Usage: tt [COMMAND [ARGS]]
Commands:
help            show this help
dash            show time dashboard, redisplaying when timelog files change
status          show current time status
what            what happened ? Show largest balances first, today and depth 1 by default
dots            print line of N dots, grouped in 4s (suitable for timedot)
x               horizontal time summary this year, monthly by default
y               vertical time summary this year, monthly by default
rweeks          recent weeks' time budgets
weeks           this and last week's time budgets
hours           show a bar chart of daily hours
accunused       show unused / undeclared accounts
accunusedcat    show unused / undeclared accounts by category
accadd          add declarations for all undeclared accounts
budgets         show monthly time budget performance this year
budgetsy        show monthly time budget performance this year, vertically
budgetsw        show weekly time budget performance this year
budgetswx       show weekly time budget performance this year, horizontally
OTHERCMD        [ARGS] run other hledger commands on $TIMELOG

Add hledger options to customise reports.

justfile

https://github.com/casey/just is like make, but easier and more suitable for running commands. It is a nice tool for organising financial reports and scripts! More on hledger and just.

Here is a justfile reimplementing the ft and tt scripts more simply:

$ brew install just  # eg
$ alias j=just
$ cd ~/finance
$ cp ~/src/hledger/bin/justfile .  # or start from scratch: just --init
$ j
justfile commands:
    watch CMD                      # rerun the given command with watchexec whenever local files change
    get-csv                        # download auto-downloadable CSVs (paypal)
    import-dry                     # import new downloaded transactions to the main journal, dry run
    import                         # import new downloaded transactions to the journal, logging and not printing errors
    get-prices *PRICEHISTFETCHOPTS # show prices for main commodities (default: today's)
    bs *HLEDGERARGS                # show balance sheet
    is *HLEDGERARGS                # show income statement
    a *HLEDGERARGS                 # show assets
    r *HLEDGERARGS                 # show revenues
    x *HLEDGERARGS                 # show expenses
    ab *HLEDGERARGS                # show assets bar chart
    rb *HLEDGERARGS                # show revenues bar chart
    xb *HLEDGERARGS                # show expenses bar chart
    al *HLEDGERARGS                # show assets line chart
    rl *HLEDGERARGS                # show revenues line chart
    xl *HLEDGERARGS                # show expenses line chart
    forecast *HLEDGERARGS          # print transactions predicted by forecast rules from last week on
    household *HLEDGERARGS         # show a draft month-end household adjustment transaction for last month
    consulting *HLEDGERARGS        # show consulting revenue
    tdash *HLEDGERARGS             # show time dashboard, redisplaying when timelog files change
    tstatus *HLEDGERARGS           # show current time status
    twhat *HLEDGERARGS             # what happened ? Show largest time balances first, today and depth 1 by default
    tdots N                        # print line of N dots, grouped in 4s (suitable for timedot)
    tx *HLEDGERARGS                # horizontal time summary this year, monthly by default
    ty *HLEDGERARGS                # vertical time summary this year, monthly by default
    tweeks *HLEDGERARGS            # this and last week's time budgets
    tweekspast *HLEDGERARGS        # recent past weeks' time budgets
    thours *HLEDGERARGS            # show a bar chart of daily hours
    taccunused *HLEDGERARGS        # show unused / undeclared time accounts
    taccunusedcat *HLEDGERARGS     # show unused / undeclared time accounts by category
    taccadd *HLEDGERARGS           # add declarations for all undeclared time accounts
    tbudgets *HLEDGERARGS          # show monthly time budget performance this year
    tbudgetsy *HLEDGERARGS         # show monthly time budget performance this year, vertically
    tbudgetsw *HLEDGERARGS         # show weekly time budget performance this year
    tbudgetswx *HLEDGERARGS        # show weekly time budget performance this year, horizontally

watchaccounts

watchaccounts shows hledger account names, updating on file change under the current directory. Arguments are passed to the hledger accounts command. Useful when cleaning up accounts.

$ watchaccounts expenses -2
$ watchaccounts -f time.journal client1 date:thismonth -l

sortandmergepostings

sortandmergepostings is an adventuresome awk script intended to clean up and merge similar postings in a transaction (see original discussion). It sorts postings so that positive ones are first, negative ones last. Within each sign, postings are sorted alphabetically by account name. Lastly if there are multiple postings to the same account in the same direction, it tries to merge them (by leaving some amounts blank). Piping the output to hledger print can recalculate the missing amounts. Multiple runs might be needed to clean up all duplicates.

$ sortandmergepostings input.journal | hledger -f - print -x

hledger-simplebal

hledger-simplebal shows how to reliably report a single machine-readable number with hledger. This and the other "hledger-" scripts are add-on commands.

$ hledger simplebal

hledger-bar

hledger-bar prints quick bar charts in the terminal.

$ hledger bar reimbursement
2023-01	++++++
2023-02	++
2023-03	++
2023-04	-------
$ hledger bar                                        # show help
$ hledger bar food                                   # monthly food expenses
$ hledger bar -- 1 --count food                      # monthly food posting counts
$ hledger bar -- type:c not:tag:clopen cur:\\\\$ -W  # weekly cashflow, $ only
$ hledger bar -- type:al not:tag:clopen cur:\\\\$    # monthly net worth change ($)
$ hledger bar -- type:rx --invert cur:\\\\$          # monthly profit/loss ($)
$ hledger bar -- -v 1 -f $TIMELOG -D                 # daily hours, with numbers

hledger-git

hledger-git provides easy version control for your journal files, using git. Run it with no arguments for help.

$ hledger git log
$ hledger git status
$ hledger git record [MSG]

hledger-pijul

hledger-pijul provides the same thing using the pijul version control system..

$ hledger pijul log
$ hledger pijul status
$ hledger pijul record [MSG]

hledger-edit

The hledger-utils python package provides a hledger-edit command to edit the queried transactions in your $EDITOR no matter what file they reside in.

Install or upgrade:

$ pip install -U hledger-utils    # might be slightly different on your system

Examples:

# Opens your $EDITOR or $VISUAL with only costs in Florida 
# (if you named and tagged them like that)
# edit the transactions, save and exit your editor, 
# then the changes are distributed to the original files
$ hledger edit Cost tag:location=Florida
# Automate changes by setting `$EDITOR` to a script
# (here all food we had on that one day in Florida was Fast Food 🌭 and we initially forgot to write that) 
EDITOR='perl -pi -e "s|Cost:Food|Cost:Food:Fast Food|g"' hledger edit tag:location=Florida date:2022-12-20

asciicast

hledger-plot

The hledger-utils python package provides a hledger-plot command for generating charts with matplotlib.

Install or upgrade:

$ pip install -U hledger-utils    # might be slightly different on your system

Examples:

$ hledger-plot -h
$ hledger plot -- bal -DH ^Assets -2

hledger-lots

hledger-lots shows a lots report, or generates a lot sale transaction, using FIFO strategy (and without needing subaccounts for lots).

Install or upgrade:

$ pip install -U hledger-lots

Examples:

$ hledger lots
$ hledger lots view
$ hledger lots list

hledger-report1.sh

hledger-report1.sh is a custom compound report done in shell. See also hledger-report1.hs.

HLEDGER-INTEGRATED

These Haskell scripts use the hledger-lib API for maximum power and robustness; they can do anything hledger's built-in commands can do.

hledger-script-example

hledger-script-example.hs is a template for writing your own hledger-integrated add-on command. It has the same structure as most of the add-ons here:

  • a stack script for robustness
  • providing command line help
  • accepting common hledger options

hledger-print-location

hledger-print-location.hs is a variant of hledger's print command that adds the file and line number to every transaction, as a tag:

$ hledger print-location -f hledger/examples/sample.journal desc:eat
2008/06/03 * eat & shop
  ; location: /Users/simon/src/hledger/examples/sample.journal:30
  expenses:food                  $1
  expenses:supplies              $1
  assets:cash

hledger-swap-dates

hledger-swap-dates.hs prints transactions with their date and date2 fields swapped.

hledger-check-tagfiles

hledger-check-tagfiles.hs interprets all tag values containing a / (forward slash) as file paths, and checks that those files exist. hledger-check-tagfiles.cabal.hs is the same command implemented as a cabal script rather than a stack script.

hledger-register-max

hledger-register-max.hs runs a register report and prints the posting with largest historical balance.

$ hledger-register-max -f examples/bcexample.hledger checking
2013-01-03 Hoogle | Payroll  Assets:US:BofA:Checking      1350.60 USD  8799.22 USD
$ hledger register-max -- -f examples/bcexample.hledger checking
2013-01-03 Hoogle | Payroll  Assets:US:BofA:Checking      1350.60 USD  8799.22 USD

hledger-check-postable

hledger-check-postable.hs check that no postings are made to accounts declared with a postable:n or postable:no tag. This can be used as a workaround when you must declare a parent account to control display order, but you don't want to allow postings to it. Eg, to allow postings to assets:cash but not assets (remember that account tags are inherited):

account assets         ; postable:n
account assets:cash    ; postable:

hledger-check-fancyassertions

hledger-check-fancyassertions.hs checks account balances over time in more complex ways than hledger's built-in balance assertions.

hledger-combine-balances

hledger-combine-balances.hs shows balance reports for two different periods side by side.

hledger-balance-as-budget

hledger-balance-as-budget.hs uses one balance report to set budget goals for another balance report.

hledger-smooth

hledger-smooth.hs is an incomplete attempt at automatically splitting infrequent/irregular transactions.

hledger-move

hledger-move.hs helps make subaccount/cost-preserving transfers.

hledger-report1.hs

hledger-report1.hs is a custom compound report done in haskell. See also hledger-report1.sh.

hledger-txnsbycat.hs

hledger-txnsbycat.hs is a mixture of a balance report and a register report; it shows each account's transactions under the account's balance.

HOW TO

Install scripts

To use these bin scripts you must ensure they are in your $PATH and runnable:

  • Shell scripts: you may need bash, or to adapt the scripts for your shell.
  • Python scripts: you'll need python 3 and pip.
  • Haskell scripts: you'll need stack (https://www.haskell.org/get-started). Or if you know how, you can make them cabal scripts, or install their dependencies manually and use runghc/ghc.

Here's a suggested install procedure:

# Go to wherever you keep financial files:
$ cd ~/finance

# Get the hledger repo
# the fast way, without version control:
$ curl -LOJ https://github.com/simonmichael/hledger/archive/refs/heads/master.zip && unzip hledger-master.zip && mv hledger-master hledger
# or the slow way, with version control for easy diffing/updating/contributing
# git clone https://github.com/simonmichael/hledger

# Make a more convenient symlink to the bin directory:
$ ln -s hledger/bin

# Add the bin directory to your PATH. Eg as a bash user:
$ echo "export PATH=$PATH:$PWD/bin" >>~/.bash_profile"
$ export PATH=$PATH:$PWD/bin

# Optionally, compile all haskell scripts for faster startup:
$ cd hledger; bin/compile.sh

# Optionally, install the python scripts:
$ pip install -U hledger-utils
$ pip install -U hledger-lots

# Check that hledger's command list now includes the bin scripts.
# Eg "check-fancyassertions" and "swap-dates" should be listed:
$ hledger


Create a new script

To create a new hledger-integrated script, copy hledger-script-example.hs. On unix, the new script should be marked executable. This should do it:

$ cd bin
$ cp hledger-script-example.hs hledger-cmd.hs   # replace cmd with your command name
# edit hledger-cmd.hs, updating at least the command name and help
$ stack install safe text     # ensure the script's dependencies are installed
$ hledger-cmd.hs --help
cmd [OPTIONS]
  My new cmd command.
  ...
$ stack ghc hledger-cmd.hs  # optionally compile for faster startup/durability
$ hledger cmd -- --help
cmd [OPTIONS]
  My new cmd command.
  ...

Run ghcid on a script

$ stack exec --package 'safe text' -- ghcid hledger-cmd.hs 
...
Ok, one module loaded.
All good (1 module, at 10:50:48)

Run ghci on a script

$ stack ghci --package 'safe text' hledger-cmd.hs 
...
Ok, one module loaded.
...
ghci> 

Learn more about scripting hledger

See Scripting hledger.

User Cookbook

Here you'll find task-oriented advice and additional user notes that didn't fit in the manual. Unlike the manual, these docs vary in age and quality.

General usage

Setups and workflows

Data entry

Preserving your data

Checking for errors

Reporting

Making charts

Customising

Other user interfaces

Scripts and add-ons

Other software

Accounting tasks

Learning accounting and bookkeeping

Borrowing and lending

Budgeting

Eco accounting

Forecasting

Inventory tracking

Investing and trading

Invoicing

Multiple currencies

Non-profit accounting

Taxes

Time tracking

Trip expenses

Shared expenses

See also

https://plaintextaccounting.org collects similar but more generic advice for all PTA tools.

Discussion / Support

PTA website plaintextaccounting.org introduces plain text accounting generally (not hledger-specific).

Chat rooms The #hledger chat room on Matrix is the best place for quick help and feedback. Follow the link and choose a Matrix client (eg the web, desktop or mobile versions of Element are good). There's also a general #plaintextaccounting room.

Or, you can join the #hledger chat on Libera IRC (older, less features). To speak, you might need to register. There's also #plaintextaccounting on Libera.

Mail list The hledger mail list; low traffic with about 200 subscribers.
Read: list.hledger.org
Join: [email protected]
Send: [email protected]

Issue tracker bugs.hledger.org (bugs only)
issues.hledger.org (all issues)
open issues by category
website issues

Reddit /r/plaintextaccounting

Mastodon #hledger and #plaintextaccounting

Hacker News stories and comments

Youtube videos (or plaintextaccounting.org's list)

Stack Exchange [hledger] on money.stackexchange.com

Twitter #hledger and #plaintextaccounting

Maintainer email [email protected]

Contributor Quick Start

New contributors are always welcome in the hledger project. Jump in! Browse the ideas below, or say hello in the chat and we'll help find you a job.

First steps

Ideas for contributing as a ...

Visitor

  • Give feedback on the site and your impressions of the project

New user

  • Give feedback on your new user experience

Developer

Developer using the hledger libraries

  • Give feedback on your experience using the hledger packages
  • Suggest API improvements

Packager

  • Improve hledger's packaging on one or more platforms

Communicator

Marketing and market understanding is vital.

  • clarify project goals, value proposition, brand, mission, story
  • monitor product-market fit
  • identify new opportunities
  • influence developer priorities
  • spread the word!

Funder

Become a financial backer to sustain and grow this project, increase your influence, express gratitude, build prosperity consciousness, and help transform world finance!

  • Use the donate links on the home page
  • Configure a recurring donation
  • Contribute or pledge bounties on issues you care about
  • Ask your organization to contribute
  • Work on project sustainability, accountability, fundraising

Tester

  • Test installation on platforms you have access to
  • Test examples, advice, and links in the docs
  • Run the latest release or developer build in daily use
  • Run tests
  • Run benchmarks
  • Report packaging, documentation, UX, functional, performance issues
  • Report and help analyse problems via irc/mail list/bug tracker

When reporting bugs, don't forget to search the tracker for a similar bug report. Otherwise, open a new bug by clicking "New issue", or http://bugs.hledger.org/new.

Enhancement requests are sometimes added to the tracker,but for these consider using the IRC channel and mail list (see Getting help). Both are archived and linkable, so the idea won't be lost. There is also a collection of wishes at the old trello board.

Bug wrangler

Tech support provider

Technical Writer

  • get familiar with the website and documentation online, review and test
  • get familiar with the site/doc source files (see Shake.hs)
  • get the latest hledger source
  • send patches with names prefixed with "doc: " (or "site: ")

Graphics Designer

  • more/better logos & graphics
  • illustrations and diagrams
  • web design mockups for home page, site, hledger-web UI

Maintainer

Help with issue management

  • watch tracker activity, report status
  • apply/update labels where needed
  • follow up on dormant issues
  • facilitate a consistently good bug-reporting & PR-contributing experience

Help with packaging

  • package hledger for linux distros, macports, etc.
  • develop mac/windows installers
  • find and assist distro packagers/installer developers

Help with project management

  • clarify/update goals and principles
  • monitor, report on project progress and performance
  • research, compare and report on successful projects, related projects
  • identify collaboration opportunities
  • marketing, communication, outreach
  • release management, roadmap planning

Developer docs

Contributor, developer, and maintainer docs. These aim to describe and communicate the structure, processes and workflows of the hledger project - "the machine that makes the machine".

These mostly are kept in doc/ in the main hledger repo, and then symlinked into the hledger_site repo for rendering on hledger.org.

Discussion: https://hledger.org/support

Github:
http://code.hledger.org, http://site.hledger.org, http://finance.hledger.org
commits, COMMITS!
http://ci.hledger.org
http://bugs.hledger.org, http://wishes.hledger.org, unknown issues, http://prs.hledger.org, http://draftprs.hledger.org, http://readyprs.hledger.org, all issues
issues with bounty tag, bountysource bounties, codemill bounties, codefund bounties
projects.hledger.org
stars.hledger.org our rank among starred haskell projects:
2024:#36
2023:#32
2022:#34
2020:#36
2018:#53
2017:#54
2016:#71

Hackage:
packages: hledger-lib, hledger, hledger-ui, hledger-web, hledger-diff, hledger-iadd, hledger-interest, hledger-irr, *hledger*
diffs: hledger-lib, hledger, hledger-ui, hledger-web
build status: hledger-lib, hledger, hledger-ui, hledger-web
reverse deps: hledger-lib, hledger, hledger-ui, hledger-web
on hackage
......
......

Stackage:
build-constraints.yaml
open hledger-related issues
packages: hledger-lib, hledger, hledger-ui, hledger-web
versions: hledger-lib, hledger, hledger-ui, hledger-web
...

Repology:
quick hledger packaging status, detailed hledger packaging status, information for hledger, all *hledger* packages

Homebrew:
hledger
our 1-year rank among homebrew installs:
2023: #1821 of 24k (top 8%)
2020: #1520 of 10k (top 15%)

Debian:
source packages: haskell-hledger-lib, bugs, haskell-hledger, bugs, haskell-hledger-ui, bugs, haskell-hledger-web, bugs
stable: hledger, bugs, hledger-ui, bugs, hledger-web, bugs
testing: hledger, bugs, hledger-ui, bugs, hledger-web, bugs
unstable: hledger, bugs, hledger-ui, bugs, hledger-web, bugs
all: *hledger*
popcon sampled install stats: haskell-hledger, hledger, hledger-ui, hledger-web

Ubuntu:
source packages: haskell-hledger-lib, bugs, haskell-hledger, bugs, haskell-hledger-ui, bugs, haskell-hledger-web, bugs
binary packages: *hledger*

Gentoo: hledger, hledger-web, *hledger*

Fedora: hledger, *hledger*, hledger (package db), Haskell SIG

Void Linux: package search->hledger

Nix: *hledger*

Sandstorm: hledger web app & reviews, issues

Reference: fosskers GHC compatibility chart

Old trello planning board: https://trello.hledger.org

hledger-web demo: https://demo.hledger.org

CREDITS

hledger is brought to you by the

  • Issue wranglers,
  • Bug hunters,
  • Design dreamers,
  • Code slingers,
  • Doc poets,
  • Package marshals,
  • Helping hands,
  • Good news preachers,
  • Bank rollers,
  • Broom pushers,

by the pioneering John Wiegley, who opened up this territory with Ledger,

and by the innumerable other benefactors making it all possible.

Commit authors

https://github.com/simonmichael/hledger/graphs/contributors

11231 commits in 16 years by 155 people as of 2022-12-21:

CommitsAuthorNotes (chat me with updates!)
9749Simon Michaelfounder, project leader, lead developer
460Stephen Morganperformance, code cleanup, runtime error removal, cli/ui output, periodic transactions, parsing, deps, valuation, --gain report, hlint, lensification
161Dmitry Astapovroi, files commands; --transpose; merge/improve --budget; generalise --forecast/--auto; docker packaging; improved CSV parsing, balancing, periodic transactions, close, parsing, docs, tests
81Vladimir Zhelezovnew bash shell completions
72Alex Chenparsing improvements, code cleanups, better error messages; dep updates
52Mykola Orliukhledger-budget, hledger-prices addons; scientific number notation; print, hledger-equity, hledger-rewrite, --pivot, space, parsing improvements; code updates; GHC 8.0 support
51Jakob Schöttlbash completions; register --invert; timeclock parsing improvements; code cleanups
40Everett Hildenbrandtdoc toolchain updates, switch from hakyll to pandoc; csv parser improvement
31Jakub Zárybnickýhledger-web, hledger-ui improvements
29Marko Kocićbuild, hlint fixes; hledger-web improvement
26Justin Lebs/cf/is improvements
26Dominik Süßhledger-web layout improvements
24Thomas R. Kollhledger-web improvements
17Peter Simonsbuild, dep fixes
17Joseph Westonparsing improvements; test, dep updates
16Aleksandar DimitrovCSV separator rule
15Nolan DarilekSandstorm app
14Brian Wignalloutput dates in YYYY-MM-DD format
12Tim Dockerparsing improvements; proper date support; P directive (first code contributor, 2008-11
12Lawrence--commodity-column; multi-day-of-week period expressions
11Trygve Laugstølhledger-web improvements; --format; CSV --rules-file, stdin support, separate in/out fields, interpolated description; test updates
11Jesse Rosenthalparsing improvementss
10Nick Ingoliainclude directive, account directive; parsing improvements (second code contributor, 2008-12
10Caleb Maclennanpayees/notes/descriptions commands; print improvement; doc, code, test updates
10Julien Moutinhoprint CSV output; hledger-equity, hledger-rewrite, hledger-web, parsing improvements; hledger-check-dates addon
10Henning Thielemannhledger-web improvements, quarter periods, code cleanup
9Ryan Desfosseswarnings, docs, hledger-web fixes
9Eli Flanaganhledger-web date picker; build, doc updates
8Imulisupport multiple input files
8Felix Yandep updates; doc updates
8Nicholas Nirobalancesheetequity command; balance tests, fix
8Moritz Kiefer--pretty-tables; dep updates; switch to megaparsec; cabal.project file; space leak fixes
7Jacob WeiszSandstorm app improvements
7Hans-Peter Deifeldep updates; csv rules parsing fix; command line parsing improvements
6Clint AdamsCSV account2 setting; dep updates; cabal test suites
6Roman Cheplyakachart command; hledger add improvements; --no-new-accounts
6Gaith Hallakundo (<) in hledger add; command line parsing improvement
6Samuel Maymulticommodity balance assertions
5Martin Michlmayrdoc updates
5Jacek Generowiczcommand line parsing fix
5Eric Mertens--pretty-tables improvements; CSV whitespace fix
5Xinruo Sunhledger-web --static-root; hledger-web autocomplete improvements; hledger print tag filtering
4Damien Cassoupayee directive, check improvements, info manual directory entries
4Michael Snoymanhledger-web improvements
4Sergey Astaninunicode support
3Michael Sanders& (AND) operator in CSV if rules
3Christian G. Wardencashflow tweaks; hledger-rewrite (auto postings) commodity substitution
3Eric Kowhledger add improvements
3toonnPR template improvements
3Carlos Lopez-Cameyhledger-web add form improvements
3Johannes Gererbalance assignments; generalise parser types
3Malte Brandy--pivot, newline in CSV rule-generated comments
2Pavlo Keresteyquoting fixes
2Alejandro García Montorohledger-web --cors
2Gergely Riskocomment directive
2aragaerfix commodity checking fix; fix --drop with csv
2Stefano Rodighierohledger-dupes addon
2Arsen Arsenovićhledger-web XSS fix
2crocketimprove hledger-ui editor support
2Arnout Engelenhledger-web register chart improvements
2gwernwhitespace, Haskell98 cleanups
2Arjen Langebaerd-c/--commodity-style option
2jungle-boogietutorial updates
2Max Bolingbrokeunicode-aware regexes; csv date-format rule
2Matthias Kauerinvestment doc improvements
2Elijah Cainegit/nix tweaks
2Judah Jacobsonreadline editing, tab completion in hledger add
2Ben Creasydoc updates
2Christoph Nicolaidoc updates
2Sergei Trofimovichbounds, build updates
2Sam Jeevesbalance assertion line number reporting
2Alex Hirzeldoc updates
2Pranesh Prakashdoc updates
1flip111
1jeevcat
1legrostdg
1max thomas
1trevorriles
1zieone
1Aerex
1ˌbodʲɪˈɡrʲim
1Aiken Cairncross
1Alan Young
1Alvaro Fernando García
1Amarandus
1Amitai Burstein
1Andreas Pauley
1Andrew Jones
1Andriy Mykhaylyk
1Ben Boeckel
1Boyd Kelly
1Brian Scott
1Bryan Richter
1Carel Fellinger
1Carl Richard Theodor Schneider
1Charlotte Van Petegem
1Colin Woodbury
1Daniel Gröber
1David D Lowe
1David Reaver
1David Zhang
1Doug Goldstein
1Evilham
1Felix Van der Jeugt
1Fun Ilrys (Nissar Chababy)
1Gabriel Ebner
1Garret McGraw
1Jan Zerebecki
1Jeff Richards
1Joachim Breitner
1Joaquin "Florius" Azcarate
1Joe Horsnell
1Johann Klähn
1John Wiegley
1Joshua Chia
1Joshua Kehn
1Kyle Marek-Spartz
1Lawrence Wu
1Luca Molteni
1Léo Gaspard
1M Parker
1Mark Hansen
1Mateus Furquim
1Michael Kainer
1Michael Walker
1Mick Dekkers
1Mitchell Rosen
1Nadrieril
1Nicolas Wavrant
1Nikhil Jha
1Nissar Chababy
1Oliver Braun
1Omari Norman
1Patrick Fiaux
1Patrik Keller
1Pavan Rikhi
1Pia Mancini
1Rick Lupton
1Rui Chen
1Sam Doshi
1ShrykeWindgrace
1Shubham Lagwankar
1Simon Hengel
1SpicyCat
1Steven R. Baker
1TANIGUCHI Kohei
1Timofey ZAKREVSKIY
1Vladimir Sorokin
1Wad
1afarrow
1agander
1awjchen
1azure-pipelines[bot]
1charukiewicz

Sponsoring hledger

Building and supporting good software and documentation is very costly; hledger comes from many thousands of skilled person-hours, sustained over 15+ years. Your support is invaluable and greatly appreciated! Is it the right time for you to help ? Consider:

  • Has this project been helpful to you or your organisation ?

  • Have you achieved enough financial success to be able to donate a little (or a lot) ?

  • Would you like to cultivate the psychological and spiritual benefits of giving back / paying forward ?

  • Would you like hledger to be around for a long time ? To remain actively supported ? To improve faster ?

  • Would you like to support our core mission ? Which is:

    To help more people achieve financial literacy, discipline and freedom, and to help grow a shared global culture of accountability and sustainability.
    (See also: FAQ)

How to sponsor

It's easy, even if not yet as efficient as we'd like. Neither hledger project nor our fiscal host is a registered charity, so your donations may not be tax-deductible, and the CFO (Simon) pays US and state income tax on all donations, in addition to the fees below.

Our fiscal host on Open Collective is The Open Source Collective, "a non-profit umbrella organisation providing financial and legal infrastructure for thousands of open source projects". So their fee is at least a sort of donation to support free/open-source software.

These donations are private and help support Simon. You can also support by offering me bounties or consulting gigs.

  • github (Fees: 0%)
  • liberapay (Fees: ~3%)
  • paypal (Fees: ~3%)
  • You can find their names at CREDITS, check their website/Github profile, and offer donations, bounties or paid work.

These donations are public and reported with hledger. They are deployed by the CFO (Simon), with input from the hledger team and community. So far, project funds have been used for project expenses, regression bounties, and one social good donation.

  • liberapay (Fees: ~3%)
  • opencollective (Fees: ~3%/$0.59 payment processor + 10% fiscal host)
  • issue bounty (simplest)
    • post a bounty pledge on an open issue
    • when resolved, pay the claimant directly (honour system).
  • bountysource bounty
    • search for the issue you want to sponsor
    • add some bounty to it
    • post a comment on the issue announcing the bounty
    • when resolved, pay through Bountysource.
  • opencollective bounty

Sponsors

We thank you, our generous sponsors, for your support, including:

REGRESSIONS

Short url for this page: hledger.org/regressions

A regression is "something that used to work, that broke", or "an unintended not-good change" in a released version of hledger.

We don't like regressions. We want to detect them quickly, repair them quickly, and reduce their frequency.

Regression bounty

You can help! Since 2021-06-14 we have offered a $100 bounty for each new regression reported in hledger releases. Since 2024-01-01, the bounty is split: $50 to the finder and $50 to the fixer (can be the same person, can't be the breaker).

To claim the bounty:

  1. Discover a new regression yourself (and don't be the one who caused it)
  2. Report it in the hledger bug tracker
  3. Wait for the issue manager (SM) confirm it with the regression label
  4. And/or, fix a regression yourself.
  5. Send an expense reimbursement request to our Open Collective. Be aware this might reveal your real name, on opencollective.com and here on hledger.org. Here's the suggested procedure for fastest processing:
    • click Submit Expense (if you are logged in, it will be under the ACTIONS menu)
    • choose Invoice
    • choose a Payout method; click Next
    • Expense title: "Regression finder bounty for #NNNN" (or "fixer" or "finder & fixer")
    • Description: "Found ISSUEURL, WORKDATE(S)" (or "Fixed ...")
    • Date: today's date
    • Amount: USD 50 (or 100 if you found and fixed). Or if you choose to receive another currency, convert from USD with that day's conversion rate, and mention the rate in Description.
    • Next, Submit Expense
  6. Announce on the issue page or in chat that you've submitted (to help ensure it is not overlooked)
  7. Wait for the finance manager (SM) to approve it. This should not take more than a day or two.
  8. Then wait for Open Collective to pay it. Payouts happen twice a week.

Regressions reported

hledger version, bug reportReporterBounty paid on
1.19 2020-09-01--
#1568jolmgpre bounty
#1688Simon MichaelN/A
#1698David Lowe2021-09-18
#1745Arne Schlüter2021-11-02
#1800Chuck Holmes2022-01-21
1.20 2020-12-05--
#1439apauleypre bounty
#1468Simon MichaelN/A
1.20.3 2021-01-14--
#1566benwebberpre bounty
1.21 2021-03-10--
#1508edlangloispre bounty
#1523Simon MichaelN/A
#1526lestephanepre bounty
#1527lestephanepre bounty
#1656Stephen Morgan2021-08-22
1.22 2021-07-03--
#1597Simon Michael2021-07-08
#1607Simon Michael2021-07-16
#1625Julian Klode2021-07-30
#1736Romain Gehrig2021-11-14
#1851Eric Langlois2022-04-11
1.22.1 2021-08-02--
#1638Yann Büchau2021-08-03
#1642Simon MichaelN/A
1.23 2021-09-21--
#1933Simon Michael2022-09-14
#2071William Pierce2024-04-02
1.24 2021-12-01--
#1782Simon MichaelN/A
1.25 2022-03-04--
#2032Simon Michael2023-05-03
#2196Pranesh Prakash
1.27 2022-09-01--
#1932Andras Fabian2022-09-15
#2018Allan Odgaard2023-03-28
1.29 2023-03-11--
#2012Simon MichaelN/A
#2020Pablo Mora2023-03-31
#2023Simon Michael2023-04-06
#2034Simon MichaelN/A
#2045Pranesh Prakash2023-10-17
#2153markokocic2024-01-25, $50 donated
1.30 2023-06-01--
#2072, #2137, #2150Simon Michael, usaAmch, ipvych
1.31 2023-09-03--
#2091Petr Slansky2023-10-16
#2115pepe_pecas2023-12-15, $100 donated
1.32 2023-12-01--
#2125Simon MichaelN/A
#2127rajeevn1
#2130Simon MichaelN/A
#2134pepe_pecas2023-12-15, $100 donated
#2156ishmaelavila

Financial reports are in the hledger_finance repo, see http://finance.hledger.org.

Finance

https://hledger.org/finance this page https://github.com/simonmichael/hledger_finance finance repo

Old:

Funding

My vision for the hledger project has always been for it to be "accountable" and "self-sustaining", possibly through new forms of incentivisation. Classic non-monetary FOSS communities are a beautiful and precious thing. Adding money can change their dynamic. Yet, we would enjoy having a lot more issues resolved, and a faster rate of progress. So we experiment, gently.

Currently we use bounties as a way to encourage resolution of issues. There are a few ways to do this:

  1. You or your organisation can offer a bounty simply by saying so on the issue.

  2. You can use Bountysource. A few hledger bounties have been completed there.

  3. You can use the new Open Collective process below.

Issues with bounties of any kind are marked with the bounty label. The Bounty Manager is @simonmichael.

New bounty process

It currently looks like this, and will evolve:

  • Issues are marked as bounties by @simonmichael. Feel free to suggest additional issues which should receive the bounty label.

  • Bounties are paid from the hledger project's public Open Collective fund. By contributing to the fund as an individual or organisation, you enable more bounties.

  • These OC bounties (unlike 1 and 2 above) have standard amounts. These may be adjusted over time, depending eg on the state of our funds. Our current bounty amounts are

    • level 1: 10 USD
    • level 2: 25 USD
    • level 3: 50 USD
  • When you complete a bounty, submit an expense to Open Collective, for whichever of the above bounty amounts you think appropriate, based eg on time or expertise spent, how much you need it, how much remains in our fund for other bounties, etc. This will be reviewed by OC and (maybe ?) @simonmichael. Successful claims, like donations, will appear in our public OC ledger.

Our bounty amounts are small, and nothing like professional rates in most countries, but they still establish a principle of sustainability, and help us to experiment. You are encouraged to claim your bounties, though you can also choose to transfer them to a new issue of your choice.

Tutorial: Accounting basics and further study

Here we'll give a quick hledger-oriented intro to some useful accounting concepts, using the journal file created in Tutorial: hledger add. Also we'll discuss account hierarchy in hledger. At the end, there's a collection of useful links to learn more.

Debits and Credits

Double-entry bookkeeping traditionally names movements of money as "debits" or "credits". As an error-checking mechanism, the debits must exactly balance the credits, both within each individual transaction and over all transactions.

Signed numbers

hledger and most other plain text accounting tools use positive and negative sign instead of the debit and credit labels. This is essentially the same system, but most people find it easier to learn than the debit/credit terminology. Positive numbers are debits, negative numbers are credits, and within each transaction (and over all transactions) the sum of amounts must be zero.

As a consequence in hledger and similar PTA tools, it's common for reports to show equity, liability, and revenue (income) balances as negative numbers. (Some hledger reports/options can show them as positive for readability.)

Here is Ledger's discussion of this.

Assets, Liabilities and Equity

Accounting describes the status of a business, person or other entity at any point in time in terms of three amounts:

  • Assets - Things owned
  • Liabilities - Things owed
  • Equity - The amount invested by owners/shareholders

The foundation of double-entry accounting is the accounting equation, which says Equity is always equal to Assets minus Liabilities (or, Net Assets).

This is also written as: Assets = Liabilities + Equity. Another way to say it: what the entity owns is funded either by debt or by the capital provided by its owners.

These three are called the Balance Sheet accounts. Their balances summarise the overall financial status at some point in time.

Revenue and Expenses

Two more amounts are used to describe changes in the above during a given period:

  • Revenue - Money flowing in
  • Expenses - Money flowing out

You may be accustomed to using the word Income instead Revenue. That's fine, just remember that Income is sometimes used to mean Net Income, which is Revenue - Expenses.

These two are called the Income Statement accounts. The balances they accumulate during some period of time indicate the inflows and outflows during that period (which will affect the Assets and Liabilities balances).

Chart of Accounts

Five numbers do not give a lot of detail. If you want to know what portion of expenses went to buy food, you could add up just the transactions with (say) "supermarket" in their description. You know how to do this with hledger:

$ hledger register desc:supermarket expenses
2015/05/25 trip to the super..  expenses                       $10           $10

But descriptions are irregular, and as you can see we missed the $5 purchase on the following day.

Instead, the major "top-level" accounts above are subdivided into subaccounts which can be used in transactions, thereby categorising them in a more structured way. If needed, these subaccounts can be subdivided further. This tree of accounts is called the Chart of Accounts. Here's a simple example where assets, revenue and expenses each have a few subaccounts:

assets
  checking
  cash
liabilities
equity
revenue
  business income
  gifts received
expenses
  food
  rent
  supplies

In some organisations and accounting systems (eg, QuickBooks), the tree structure is de-emphasised, so the above is represented more like:

 Account name      Account type
 ------------------------------- 
 checking          ASSET
 cash              ASSET
 business income   REVENUE
 gifts received    REVENUE
 food              EXPENSE
 rent              EXPENSE
 supplies          EXPENSE

In others, the tree structure is encoded as decimal account numbers, something like this:

1000 assets
1100   checking
1200   cash
2000 liabilities
3000 equity
4000 revenue
4100   business income
4200   gifts received
5000 expenses
5100   food
5200   rent
5300   supplies

A digression: subaccounts in hledger

With hledger, tree structure is implied by writing account names like ACCOUNT:SUBACCOUNT. Try it: edit your journal file and change the account names like so:

$ cat ~/.hledger.journal

2015/05/25 trip to the supermarket
    expenses:supplies           $10
    assets:checking            $-10

2015/05/26 forgot the bread
    expenses:food            $5
    assets:cash

hledger will infer the chart of accounts from these names. The accounts command will list all accounts posted to:

$ hledger accounts
assets:cash
assets:checking
expenses:food
expenses:supplies

and accounts --tree will show the tree structure, indenting subaccounts below their parents (and eliding the common part of their names):

assets
  cash
  checking
expenses
  food
  supplies

Conversely, the balance command shows the tree structure by default:

$ hledger balance
                $-15  assets
                 $-5    cash
                $-10    checking
                 $15  expenses
                  $5    food
                 $10    supplies
--------------------
                   0

As you can see, the balance reported for parent accounts includes the balances of any subaccounts (it would also include any postings to the parent account itself.)

To see full account names in a flat list, use --flat:

$ hledger balance --flat
                 $-5  assets:cash
                $-10  assets:checking
                  $5  expenses:food
                 $10  expenses:supplies
--------------------
                   0

hledger accepts whatever account names you choose, so you can use as much or as little account hierarchy as you need. Most users have at least two levels of accounts. You can limit the amount of detail in a balance report by hiding accounts below a certain depth:

$ hledger balance --depth 1
                $-15  assets
                 $15  expenses
--------------------
                   0

General

Video

Theory

History

Balancing the accounting equation

The Accounting Equation states that Assets and Liabilities always match Equity. Eg: A - L = E.

This suggests that a balance report showing all Asset, Liability and Equity account balances should show a zero grand total. With hledger you can check this with a balance report like:

$ hledger balance ^assets ^liabilities ^equity

or more easily with the balancesheetequity command, which is designed for this:

$ hledger bse

Note, checking the accounting equation is different from checking a trial balance. A trial balance just checks that the total inflows and outflows over all accounts are equal, which can be seen by a zero grand total for hledger balance. Normally this is ensured by hledger's requirement that each individual transaction is balanced, but some of the same problems noted below apply to this also.

Common problems

In practice, you will find quite a number of things in real-life journals can disrupt the accounting equation and cause a non-zero total. Note, this does not interfere with most day-to-day reporting, and many PTA users won't notice it as a problem. But, seeing the correct zero total gives added confidence in your bookkeeping, for yourself and others you might be sharing reports with.

Here are some things that disturb the accounting equation, and their solutions:

1. Unclosed revenue/expenses

Revenues (income) and expenses are technically part of equity. In traditional accounting, they should be transferred to an account like equity:retained earnings at the end of each reporting period.

You could record such transfers in your journal, either manually or using the close command. Most PTA users don't bother with this.

More conveniently, you can use an account alias to convert revenue/expense accounts to equity temporarily. Eg:
--alias '/^(revenues|income|expenses)\b/=equity'

2. Unbalanced conversions with cost notation (@/@@)

Currency/commodity conversions using @/@@ notation and no equity conversion postings are unbalanced. You can rewrite them in balanced form, eg combining costs and equity conversion postings.

Or, use --infer-equity to do this temporarily at report time.

Or, converting amounts to cost may be another solution - try adding -B.

3. Rounding error with unit costs (@) and --infer-equity

--infer-equity is convenient but it tends to expose inaccuracies in the recorded @ prices, causing small non-zero values in the total. You can ignore this, or try to fix it by making @ prices more accurate, or replace your uses of @ with @@ (?) or equity postings.

4. Posting dates

Postings dates different from their transaction's date (; date:DATE or ; [DATE] notation) cause an imbalance in the accounting equation between the transaction date and posting date. Usually these unbalanced periods are short and do not cross a file boundary, so you can just avoid them when testing the accounting equation.

If they do cross a file boundary, or are inconveniently long, you can fix that by splitting the transaction into two transactions which use a pending account, as in close and balance assertions.

5. Unbalanced postings

Unbalanced virtual postings (with parenthesised account names) create an imbalance by definition; just exclude them from the report with -R/--real. This also excludes balanced virtual postings (with bracketed account names), but that will probably be harmless.

6. Partial reports

Many kinds of report query exclude some data, which can disturb the accounting equation. So, it's best to avoid queries when checking the accounting equation. If you specify a report start date, be sure to include balances from previous transactions by adding -H/--historical. (Or use the bse command, which does this automatically.)

An improved accounting equation report

Combining these, here is a more robust command for checking the accounting equation:

$ hledger bse -R --infer-equity --alias '/^(revenues|income|expenses)\b/=equity' not:desc:'closing balances' --layout tall -f YYYY.journal
  • -R - (--real) excludes any unbalanced virtual postings
  • --infer-equity - adds equity postings where needed to balance transactions using @/@@ cost notation
  • --alias ... - recategorises all revenues/expenses as equity
  • not:desc:... - excludes any final closing balance transactions that would hide ending balances (suitable for checking a single journal file)
  • --layout tall - improves readability when there are many commodities
  • -f ... - optional, specifies a file other than the default $LEDGER_FILE.

Borrowing and Lending

Lending, calculating interest manually

0.41% interest per month (roughly equivalent to 5% APR), calculated manually:

2020-01-01 opening balances
  assets:bank:checking   1000
  equity:opening/closing balances

2020-01-01 lend to Trusty Tara
  assets:bank:checking
  assets:receivable:tt    100
  
2020-02-01 charge 5% interest
  assets:receivable:tt      0.41   ; 100 x 0.41
  revenues:interest:tt

2020-02-15 Tara payment
  assets:receivable:tt    -50
  assets:bank:checking

2020-03-01 charge 5% interest
  assets:receivable:tt      0.21   ; 50.41 x 0.41, rounded
  revenues:interest:tt

2020-03-15 Tara payment
  assets:receivable:tt    -50
  assets:bank:checking

Monthly balance sheet:

$ hledger bs -M
Balance Sheet 2020-01-31,,2020-03-31

                      || 2020-01-31  2020-02-29  2020-03-31 
======================++====================================
 Assets               ||                                    
----------------------++------------------------------------
 assets:bank:checking ||     900.00      950.00     1000.00 
 assets:receivable:tt ||     100.00       50.41        0.62 
----------------------++------------------------------------
                      ||    1000.00     1000.41     1000.62 
======================++====================================
 Liabilities          ||                                    
----------------------++------------------------------------
----------------------++------------------------------------
                      ||                                    
======================++====================================
 Net:                 ||    1000.00     1000.41     1000.62 

Lending, calculating interest with hledger-interest

Loan and payment transactions are in the main journal:

2020-01-01 opening balances
  assets:bank:checking   1000.00
  equity:opening/closing balances

2020-01-01 lend to Trusty Tara
  assets:bank:checking
  assets:receivable:tt    100 = 100
  
2020-02-15 Tara payment
  assets:receivable:tt    -50
  assets:bank:checking

2020-03-15 Tara payment
  assets:receivable:tt    -50
  assets:bank:checking

We use hledger-interest to add interest transactions, here 5% per year:

$ hledger-interest assets:receivable:tt --act --annual=0.05 -s revenues:interest:tt -t assets:receivable:tt 
2020-01-01 lend to Trusty Tara
    assets:bank:checking         -100.00
    assets:receivable:tt          100.00 = 100.00

2020-02-15 5% interest for 100.00 over 46 days
    assets:receivable:tt            0.63
    revenues:interest:tt           -0.63

2020-02-15 Tara payment
    assets:receivable:tt          -50.00
    assets:bank:checking           50.00

2020-03-15 5% interest for 50.63 over 29 days
    assets:receivable:tt            0.20
    revenues:interest:tt           -0.20

2020-03-15 Tara payment
    assets:receivable:tt          -50.00
    assets:bank:checking           50.00

It doesn't print the opening balance transaction for some reason. So we'll print that too, then get a monthly balance sheet:

$ (hledger print desc:opening; hledger-interest assets:receivable:tt --act --annual=0.05 -s revenues:interest:tt -t assets:receivable:tt) | hledger -f- bs -M
Balance Sheet 2020-01-31,,2020-03-31

                      || 2020-01-31  2020-02-29  2020-03-31 
======================++====================================
 Assets               ||                                    
----------------------++------------------------------------
 assets:bank:checking ||     900.00      950.00     1000.00 
 assets:receivable:tt ||     100.00       50.63        0.83 
----------------------++------------------------------------
                      ||    1000.00     1000.63     1000.83 
======================++====================================
 Liabilities          ||                                    
----------------------++------------------------------------
----------------------++------------------------------------
                      ||                                    
======================++====================================
 Net:                 ||    1000.00     1000.63     1000.83 

Budgeting and forecasting (2018)

This is an old but still useful intro to budgeting with hledger. For more about budgeting, see also:

Budgeting and forecasting allows you to keep better track of your expenses and future financial situation. If you write down your expectations of what your income/expenses/investment yields/etc should be, you can use them to:

  • check how far off are your expectations from reality (budgeting)
  • project your future account activity or balances (forecasting)

(This section uses examples/bcexample.hledger from hledger source repository).

Goal-based budgeting

To start budgeting, you need to know what your average yearly or weekly expenditures are. Hledger could help you with that. Usually the interval for which you compute budget figures will be the same as the interval between your paychecks -- monthly or weekly.

Lets create monthly (-M) report for years 2013-2014 (-b 2013) of all top-level expense categories (--depth 2 Expenses), looking for average figures (-A), limiting ourselves to USD transactions only, to save screen space:

$ hledger balance -f bcexample.hledger -MA -b 2013 --depth 2 Expenses cur:USD
Balance changes in 2013/01/01-2014/10/31:

                    ||     2013/01      2013/02      2013/03  ...      2014/07      2014/08      2014/09      2014/10      Average 
====================++========================================...==================================================================
 Expenses:Financial ||    4.00 USD    12.95 USD    39.80 USD  ...    30.85 USD    21.90 USD    12.95 USD     4.00 USD    17.83 USD 
 Expenses:Food      ||  396.46 USD   481.48 USD   603.32 USD  ...   871.20 USD   768.23 USD   466.72 USD    83.00 USD   562.10 USD 
 Expenses:Health    ||  290.70 USD   193.80 USD   193.80 USD  ...   290.70 USD   193.80 USD   193.80 USD    96.90 USD   207.01 USD 
 Expenses:Home      || 2544.98 USD  2545.02 USD  2544.97 USD  ...  2545.12 USD  2545.01 USD  2545.10 USD            0  2429.33 USD 
 Expenses:Taxes     || 5976.60 USD  3984.40 USD  4901.83 USD  ...  5976.60 USD  3984.40 USD  3984.40 USD  1992.20 USD  4322.27 USD 
 Expenses:Transport ||  120.00 USD   120.00 USD   120.00 USD  ...            0   120.00 USD   120.00 USD   120.00 USD   109.09 USD 
--------------------++----------------------------------------...------------------------------------------------------------------
                    || 9332.74 USD  7337.65 USD  8403.72 USD  ...  9714.47 USD  7633.34 USD  7322.97 USD  2296.10 USD  7647.64 USD 

This report is rather wide and portion of it had been cut out for brevity. Most interesting column is the last one, it shows average monthly expenses for each category. Expenses in Food, Health, Home and Transport categories seem to roughly similar month to month, so lets create a budget for them.

Budgets are described with periodic (ie, recurring) transaction rules. Periodic transaction has ~ instead of date and period expression instead of description. In this case we want to create a monthly budget that will come into effect starting from January 2013, which will include income of 10000 USD that is partially spent on Food, Health, Home and Transport and the rest becomes our Assets:

~ monthly from 2013/01
  Expenses:Food    500 USD
  Expenses:Health  200 USD
  Expenses:Home    2545 USD
  Expenses:Transport   120 USD
  Income:US        -10700 USD ;; Taken as monthy average of Income account group
  Assets:US

This transaction could be put into separate file (budget.journal) or could be kept in the main journal. Normally hledger will ignore it and will not include it in any computations or reports.

To put it into action, you need to add --budget switch to your balance invocation. If you do that, you would be able to see how your past expenses aligned with the budget that you just created. This time, lets not limit accounts in any way:

$ hledger balance -f bcexample.hledger -f budget.journal -MB -b 2013 --budget cur:USD
Balance changes in 2013/01/01-2014/10/31:

                          ||                            2013/01                            2013/02                             2013/03 
==========================++===========================================================================================================
 <unbudgeted>:Expenses    ||                        5980.60 USD                        3997.35 USD                         4941.63 USD 
 <unbudgeted>:Liabilities ||                         293.09 USD                        -147.51 USD                          -66.01 USD 
 Assets:US                ||      1893.32 USD [26% of 7335 USD]      2929.77 USD [40% of 7335 USD]     -3898.89 USD [-53% of 7335 USD] 
 Expenses:Food            ||        396.46 USD [79% of 500 USD]        481.48 USD [96% of 500 USD]        603.32 USD [121% of 500 USD] 
 Expenses:Health          ||       290.70 USD [145% of 200 USD]        193.80 USD [97% of 200 USD]         193.80 USD [97% of 200 USD] 
 Expenses:Home            ||     2544.98 USD [100% of 2545 USD]     2545.02 USD [100% of 2545 USD]      2544.97 USD [100% of 2545 USD] 
 Expenses:Transport       ||       120.00 USD [100% of 120 USD]       120.00 USD [100% of 120 USD]        120.00 USD [100% of 120 USD] 
 Income:US                || -15119.10 USD [141% of -10700 USD]  -10331.21 USD [97% of -10700 USD]  -11079.40 USD [104% of -10700 USD] 
--------------------------++-----------------------------------------------------------------------------------------------------------
                          ||                       -3599.95 USD                        -211.30 USD                        -6640.58 USD 

Numbers in square brackets give you your budget estimate and percentage of it used by your real expenses. Numbers below 100% mean that you have some of your budget left, numbers over 100% mean that you went over your budget.

You can notice that actual numbers for Assets:US seem to be well below computed budget of 7335 USD. Why? Answer to this is in the first row of the report: we have quite a lot of unbudgeted Expenses!

Notice that even though we have not limited accounts in any way, report includes just those mentioned in the budget. This is on purpose, assumption is that when you are checking your budgets you probably do not want unbudgeted accounts getting in your way. Another thing to note is that budget numbers have been allocated to top-level expense subcategories (like Expenses:Food). Journal has subaccounts under Food, but to compute budget report they have all been rolled up into a nearest parent with budget number associated with it. Accounts that do not have such parent went into <unbudgeted> row.

Allright, it seems that for Jan 2013 we have ~3000 USD of budgeted expenses and almost twice as much unbudgeted. Lets figure out what they are. We can see more details if we add -E/--empty switch:

$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2013-01 -e 2013-02 --budget cur:USD -E
Balance changes in 2013/01:

                                  ||                            2013/01 
==================================++====================================
 Assets:US                        ||      1893.32 USD [26% of 7335 USD] 
 Expenses:Financial:Fees          ||                           4.00 USD 
 Expenses:Food                    ||        396.46 USD [79% of 500 USD] 
 Expenses:Health                  ||       290.70 USD [145% of 200 USD] 
 Expenses:Home                    ||     2544.98 USD [100% of 2545 USD] 
 Expenses:Taxes:Y2013:US:CityNYC  ||                         524.76 USD 
 Expenses:Taxes:Y2013:US:Federal  ||                        3188.76 USD 
 Expenses:Taxes:Y2013:US:Medicare ||                         319.86 USD 
 Expenses:Taxes:Y2013:US:SDI      ||                           3.36 USD 
 Expenses:Taxes:Y2013:US:SocSec   ||                         844.62 USD 
 Expenses:Taxes:Y2013:US:State    ||                        1095.24 USD 
 Expenses:Transport               ||       120.00 USD [100% of 120 USD] 
 Income:US                        || -15119.10 USD [141% of -10700 USD] 
 Liabilities:US:Chase:Slate       ||                         293.09 USD 
----------------------------------++------------------------------------
                                  ||                       -3599.95 USD 

All the accounts that were rolled up into <unbudgeted> category are now shown with their original name, but budgeted accounts are still rolled up. It is easy to see now that we forgot taxes. Lets add them to our budget:

~ monthly from 2013/01
  Expenses:Food    500 USD
  Expenses:Health  200 USD
  Expenses:Home    2545 USD
  Expenses:Transport   120 USD
  Expenses:Taxes   4300 USD ;; Taken from monthly average report
  Income:US        -10700 USD
  Assets:US

Lets try again for a couple of month with this updated budget:

$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2013-01 -e 2013-04 --budget cur:USD 
Balance changes in 2013q1:

                          ||                            2013/01                            2013/02                             2013/03 
==========================++===========================================================================================================
 <unbudgeted>:Expenses    ||                           4.00 USD                          12.95 USD                           39.80 USD 
 <unbudgeted>:Liabilities ||                         293.09 USD                        -147.51 USD                          -66.01 USD 
 Assets:US                ||      1893.32 USD [62% of 3035 USD]      2929.77 USD [97% of 3035 USD]    -3898.89 USD [-128% of 3035 USD] 
 Expenses:Food            ||        396.46 USD [79% of 500 USD]        481.48 USD [96% of 500 USD]        603.32 USD [121% of 500 USD] 
 Expenses:Health          ||       290.70 USD [145% of 200 USD]        193.80 USD [97% of 200 USD]         193.80 USD [97% of 200 USD] 
 Expenses:Home            ||     2544.98 USD [100% of 2545 USD]     2545.02 USD [100% of 2545 USD]      2544.97 USD [100% of 2545 USD] 
 Expenses:Taxes           ||     5976.60 USD [139% of 4300 USD]      3984.40 USD [93% of 4300 USD]      4901.83 USD [114% of 4300 USD] 
 Expenses:Transport       ||       120.00 USD [100% of 120 USD]       120.00 USD [100% of 120 USD]        120.00 USD [100% of 120 USD] 
 Income:US                || -15119.10 USD [141% of -10700 USD]  -10331.21 USD [97% of -10700 USD]  -11079.40 USD [104% of -10700 USD] 
--------------------------++-----------------------------------------------------------------------------------------------------------
                          ||                       -3599.95 USD                        -211.30 USD                        -6640.58 USD 

Now unbudgeted amounts are much smaller and some of them could be dismissed as noise, and we can see that budget created is actually close enough to the real numbers, meaning that they are usually close to average that we put in our budget.

Budget report that we have used so far assumes that any unused budget amount for a given (monthly) period will not contribute to the budget of the next period. Alternative popular "envelope budget" strategy assumes that you put a certain amount of money into an envelope each month, and any unused amount stays there for future expenses. This is easy to simulate by adding --cumulative switch. Lets redo the last report with it:

$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2013-01 -e 2013-04 --cumulative --budget cur:USD
Ending balances (cumulative) in 2013q1:

                          ||                         2013/01/31                          2013/02/28                          2013/03/31 
==========================++============================================================================================================
 <unbudgeted>:Expenses    ||                           4.00 USD                           16.95 USD                           56.75 USD 
 <unbudgeted>:Liabilities ||                         293.09 USD                          145.58 USD                           79.57 USD 
 Assets:US                ||      1893.32 USD [62% of 3035 USD]       4823.09 USD [79% of 6070 USD]        924.20 USD [10% of 9105 USD] 
 Expenses:Food            ||        396.46 USD [79% of 500 USD]        877.94 USD [88% of 1000 USD]       1481.26 USD [99% of 1500 USD] 
 Expenses:Health          ||       290.70 USD [145% of 200 USD]        484.50 USD [121% of 400 USD]        678.30 USD [113% of 600 USD] 
 Expenses:Home            ||     2544.98 USD [100% of 2545 USD]      5090.00 USD [100% of 5090 USD]      7634.97 USD [100% of 7635 USD] 
 Expenses:Taxes           ||     5976.60 USD [139% of 4300 USD]      9961.00 USD [116% of 8600 USD]    14862.83 USD [115% of 12900 USD] 
 Expenses:Transport       ||       120.00 USD [100% of 120 USD]        240.00 USD [100% of 240 USD]        360.00 USD [100% of 360 USD] 
 Income:US                || -15119.10 USD [141% of -10700 USD]  -25450.31 USD [119% of -21400 USD]  -36529.71 USD [114% of -32100 USD] 
--------------------------++------------------------------------------------------------------------------------------------------------
                          ||                       -3599.95 USD                        -3811.25 USD                       -10451.83 USD 

If you look at Expenses:Food category, you will see that every month budget is increased by 500 USD, and by March total amount budgeted is 1500 USD, of which 1481.26 USD is spent. If you look back at the previous non-cumulative monthly budget report, you will see that in March food expenses were 121% of the budgeted amount, but cumulative report shows that taking into account budget carry-over from Jan and Feb we are well within planned numbers.

Envelope budgeting

Real envelope budgeting involves actually setting aside money for each category and spending only from there. In physical envelope budgeting, there are actual envelopes of cash. When doing envelope budgeting with hledger, the envelopes are represented by subaccounts, into which you transfer money and from which you spend it.

Note with this style of budgeting, you don't use periodic transactions or the --budget report - just regular transfers and regular balance reports. At the end of each period you can decide to remove or reallocate any surpluses, or let them roll over.b

A good place to keep the envelope accounts is under your regular checking or cash account, since it keeps the overall balance correct. Eg assets:checking:rent, assets:checking:food etc. Some people use virtual (imaginary) accounts instead.

The advantage of envelope budgeting is that it models your available funds precisely, which can be important in tight cashflow situations. hledger doesn't prevent overspending from your budget envelopes - you should watch their balances and make sure they never go negative. (The hledger-check-fancyassertions script could help, https://hledger.org/scripts.html.)

The downside is that it requires more bookkeeping work from you. Some people use auto posting rules to try to reduce that, but this can add complexity.

For more about envelope budgeting, see the links at plaintextaccounting.org/budgeting.

Forecasting

Budget transaction that was created could be used to predict what would be our financial situation in the future. If you add --forecast switch, you will see how budgeted income and expense affects you past the last transaction in the journal. Since journal ends in Oct 2014, lets see next two month:

$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2014-10 -e 2015 --forecast cur:USD
Balance changes in 2014q4:

                                    ||      2014/10     2014/11     2014/12 
====================================++======================================
 Assets:US                          ||            0    3035 USD    3035 USD 
 Assets:US:BofA:Checking            || -2453.40 USD           0           0 
 Assets:US:ETrade:Cash              ||  5000.00 USD           0           0 
 Expenses:Financial:Fees            ||     4.00 USD           0           0 
 Expenses:Food                      ||            0     500 USD     500 USD 
 Expenses:Food:Restaurant           ||    83.00 USD           0           0 
 Expenses:Health                    ||            0     200 USD     200 USD 
 Expenses:Health:Dental:Insurance   ||     2.90 USD           0           0 
 Expenses:Health:Life:GroupTermLife ||    24.32 USD           0           0 
 Expenses:Health:Medical:Insurance  ||    27.38 USD           0           0 
 Expenses:Health:Vision:Insurance   ||    42.30 USD           0           0 
 Expenses:Home                      ||            0    2545 USD    2545 USD 
 Expenses:Taxes                     ||            0    4300 USD    4300 USD 
 Expenses:Taxes:Y2014:US:CityNYC    ||   174.92 USD           0           0 
 Expenses:Taxes:Y2014:US:Federal    ||  1062.92 USD           0           0 
 Expenses:Taxes:Y2014:US:Medicare   ||   106.62 USD           0           0 
 Expenses:Taxes:Y2014:US:SDI        ||     1.12 USD           0           0 
 Expenses:Taxes:Y2014:US:SocSec     ||   281.54 USD           0           0 
 Expenses:Taxes:Y2014:US:State      ||   365.08 USD           0           0 
 Expenses:Transport                 ||            0     120 USD     120 USD 
 Expenses:Transport:Tram            ||   120.00 USD           0           0 
 Income:US                          ||            0  -10700 USD  -10700 USD 
 Income:US:Hoogle:GroupTermLife     ||   -24.32 USD           0           0 
 Income:US:Hoogle:Salary            || -4615.38 USD           0           0 
 Liabilities:US:Chase:Slate         ||  -203.00 USD           0           0 
------------------------------------++--------------------------------------
                                    ||            0           0           0 

Note that this time there is no roll-up of accounts. Unlike --budget, which could be used with balance command only, --forecast could be used with any report. Forecast transactions would be added to your real journal and would appear in the report you requested as if you have entered them on the scheduled dates.

Since quite a lot of accounts do not have any budgeted transactions, lets limit the depth of the report to avoid seeing lots of zeroes:

$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2014-10 -e 2015 --forecast cur:USD --depth 2
Balance changes in 2014q4:

                    ||      2014/10     2014/11     2014/12 
====================++======================================
 Assets:US          ||  2546.60 USD    3035 USD    3035 USD 
 Expenses:Financial ||     4.00 USD           0           0 
 Expenses:Food      ||    83.00 USD     500 USD     500 USD 
 Expenses:Health    ||    96.90 USD     200 USD     200 USD 
 Expenses:Home      ||            0    2545 USD    2545 USD 
 Expenses:Taxes     ||  1992.20 USD    4300 USD    4300 USD 
 Expenses:Transport ||   120.00 USD     120 USD     120 USD 
 Income:US          || -4639.70 USD  -10700 USD  -10700 USD 
 Liabilities:US     ||  -203.00 USD           0           0 
--------------------++--------------------------------------
                    ||            0           0           0 

As you can see, we should expect 3035 USD to be added into Assets:US each month. It is quite easy to see how overal amount of Assets will change with time if you use --cumulative switch:

$ hledger balance -f bcexample.hledger -f budget.journal -M -b 2014-10 -e 2015 --forecast cur:USD --depth 2 --cumulative
Ending balances (cumulative) in 2014q4:

                    ||   2014/10/31     2014/11/30     2014/12/31 
====================++============================================
 Assets:US          ||  2546.60 USD    5581.60 USD    8616.60 USD 
 Expenses:Financial ||     4.00 USD       4.00 USD       4.00 USD 
 Expenses:Food      ||    83.00 USD     583.00 USD    1083.00 USD 
 Expenses:Health    ||    96.90 USD     296.90 USD     496.90 USD 
 Expenses:Home      ||            0       2545 USD       5090 USD 
 Expenses:Taxes     ||  1992.20 USD    6292.20 USD   10592.20 USD 
 Expenses:Transport ||   120.00 USD     240.00 USD     360.00 USD 
 Income:US          || -4639.70 USD  -15339.70 USD  -26039.70 USD 
 Liabilities:US     ||  -203.00 USD    -203.00 USD    -203.00 USD 
--------------------++--------------------------------------------
                    ||            0              0              0 

According to forecast, assets are expected to grow to 8600+ USD by the end of 2014. However, our forecast does not include a couple of big one-off year end expenses. First, we plan to buy prize turkey for the Christmas table every year from 2014, spending up to 500 USD on it. And on 17th Nov 2014 we would celebrate birthday of significant other, spending up to 6000 USD in a fancy restaurant:

~ every 20th Dec from 2014
  Expenses:Food   500 USD ; Prize turkey, the biggest of the big
  Assets:US

~ 2014/11/17
  Assets:US
  Expenses:Food   6000 USD ; Birthday, lots of guests 

Note that turkey transaction is not entered as "yearly from 2014/12/20", since yearly/quarterly/monthy/weekly periodic expressions always generate entries at the first day of the calendar year/quarter/month/week. Thus "monthly from 2014/12" will occur on 2014/12/01, 2015/01/01, ..., whereas "every 20th of month from 2014/12" will happen on 2014/12/20, 2015/12/20, etc.

With latest additions forecast now looks like this:

hledger balance -f bcexample.hledger -f budget.journal -M -b 2014-10 -e 2015 --forecast cur:USD --depth 2 --cumulative
Ending balances (cumulative) in 2014q4:

                    ||   2014/10/31     2014/11/30     2014/12/31 
====================++============================================
 Assets:US          ||  2546.60 USD    -418.40 USD    2116.60 USD 
 Expenses:Financial ||     4.00 USD       4.00 USD       4.00 USD 
 Expenses:Food      ||    83.00 USD    6583.00 USD    7583.00 USD 
 Expenses:Health    ||    96.90 USD     296.90 USD     496.90 USD 
 Expenses:Home      ||            0       2545 USD       5090 USD 
 Expenses:Taxes     ||  1992.20 USD    6292.20 USD   10592.20 USD 
 Expenses:Transport ||   120.00 USD     240.00 USD     360.00 USD 
 Income:US          || -4639.70 USD  -15339.70 USD  -26039.70 USD 
 Liabilities:US     ||  -203.00 USD    -203.00 USD    -203.00 USD 
--------------------++--------------------------------------------
                    ||            0              0              0 

It is easy to see that in Nov 2014 we will run out of Assets. Using register we can figure out when or why it would happen:

$ hledger register -f bcexample.hledger -f budget.journal -b 2014-10 -e 2014-12 --forecast cur:USD Assets
2014/10/04 "BANK FEES" | "Monthly bank fee"         Assets:US:BofA:Checking                      -4.00 USD     -4.00 USD
2014/10/09 "Hoogle" | "Payroll"                     Assets:US:BofA:Checking                    2550.60 USD   2546.60 USD
2014/10/10 "Transfering accumulated savings to o..  Assets:US:BofA:Checking                   -5000.00 USD  -2453.40 USD
                                                    Assets:US:ETrade:Cash                      5000.00 USD   2546.60 USD
2014/11/01 Forecast transaction                     Assets:US                                     3035 USD   5581.60 USD
2014/11/17 Forecast transaction                     Assets:US                                    -6000 USD   -418.40 USD

It is 6000 USD planned for birthday! Something will have to be done about the birthday plans.

Budgeting

Notes

 <sm> two commands that are roughly equivalent: ledger budget --add-budget expenses, hledger balance --budget -E expenses
 <sm> they show both budgeted and unbudgeted accounts            
--budget has no effect on single-column reports, it requires a reporting interval
--budget INTERVAL enables all periodic transactions with that interval; these can be date-limited
--budget hides all non-budgeted subaccounts; can be depth-limited more
<sm> there's different ways to do budgeting                     [16:46]
<sm> let me try to count them                                   [16:50]
<sm> "envelope budgeting" is analogous to having a set of envelopes containing cash for different purposes. 
You can model the "envelopes" with 
a. real-world accounts (eg your bank lets you create arbitrary savings accounts), 
b. virtual (imaginary) subaccounts of a real-world account (eg your checking account), 
c. virtual accounts "off to the side" (budget:*)
<sm> also you can do the transfers to and from these manually, or generate them with automated posting rules
<sm> "goal budgeting" (best name I can come up with) involves setting some inflow/outflow goals per account per period, 
and then measuring how the actual flows compare with the goals. balance --budget provides this report
<sm> I think that's 7 ways

From https://www.reddit.com/r/plaintextaccounting/comments/doq9p5/new_to_ledger_budgeting_question:

Also search for budgeting links at http://plaintextaccounting.org . You'll see two main approaches discussed:

  1. "envelope budgeting" - sounds more like what you've been doing. Based around explicitly allocating money for each purpose. Good for managing > cashflow. Requires more journal entries. Can be done entirely manually (1a) but many docs advise using automatic posting rules to assist (1b). Many > different ways to handle the details. Requires more thinking.

  2. the other kind ("report-based budgeting" ?). Based around a special budget report provided by Ledger/hledger, which uses periodic transaction > rules to set budget goals. Automatic posting rules might be useful here too, I'm not sure. Provides less enforcement, requires less work. Fewer ways > to do it, perhaps provides simpler/clearer reports.

I often find "budgeting" covers/touches on quite a lot of topics:

  • setting earning/spending goals,
  • reviewing performance against those goals,
  • controlling earning/spending based on the goals,
  • allocating funds for short term expenses,
  • allocating funds towards savings goals,
  • updating allocated funds as transactions occur,
  • reallocating funds/balancing the budget,
  • end of period actions (roll over ? reset ?),
  • forecasting cash balances and managing cashflow,
  • forecasting income/expenses...

--budget and subaccounts

You can add budgets to any account in your account hierarchy. If you have budgets on both parent account and some of its children, then budget(s) of the child account(s) would be added to the budget of their parent, much like account balances behave.

In the most simple case this means that once you add a budget to any account, all its parents would have budget as well.

To illustrate this, consider the following budget:

~ monthly from 2019/01
    expenses:personal             $1,000.00
    expenses:personal:electronics    $100.00
    liabilities

With this, monthly budget for electronics is defined to be $100 and budget for personal expenses is an additional $1000, which implicitly means that budget for both expenses:personal and expenses is $1100.

Transactions in expenses:personal:electronics will be counted both towards its $100 budget and $1100 of expenses:personal , and transactions in any other subaccount of expenses:personal would be counted towards only towards the budget of expenses:personal.

For example, let's consider these transactions:

~ monthly from 2019/01
    expenses:personal             $1,000.00
    expenses:personal:electronics    $100.00
    liabilities

2019/01/01 Google home hub
    expenses:personal:electronics          $90.00
    liabilities                           $-90.00

2019/01/02 Phone screen protector
    expenses:personal:electronics:upgrades          $10.00
    liabilities

2019/01/02 Weekly train ticket
    expenses:personal:train tickets       $153.00
    liabilities

2019/01/03 Flowers
    expenses:personal          $30.00
    liabilities

As you can see, we have transactions in expenses:personal:electronics:upgrades and expenses:personal:train tickets, and since both of these accounts are without explicitly defined budget, these transactions would be counted towards budgets of expenses:personal:electronics and expenses:personal accordingly:

$ hledger balance --budget -M
Budget performance in 2019/01:

                               ||                           Jan 
===============================++===============================
 expenses                      ||  $283.00 [  26% of  $1100.00] 
 expenses:personal             ||  $283.00 [  26% of  $1100.00] 
 expenses:personal:electronics ||  $100.00 [ 100% of   $100.00] 
 liabilities                   || $-283.00 [  26% of $-1100.00] 
-------------------------------++-------------------------------
                               ||        0 [                 0] 

And with --empty, we can get a better picture of budget allocation and consumption:

$ hledger balance --budget -M --empty
Budget performance in 2019/01:

                                        ||                           Jan 
========================================++===============================
 expenses                               ||  $283.00 [  26% of  $1100.00] 
 expenses:personal                      ||  $283.00 [  26% of  $1100.00] 
 expenses:personal:electronics          ||  $100.00 [ 100% of   $100.00] 
 expenses:personal:electronics:upgrades ||   $10.00                      
 expenses:personal:train tickets        ||  $153.00                      
 liabilities                            || $-283.00 [  26% of $-1100.00] 
----------------------------------------++-------------------------------
                                        ||        0 [                 0] 

Envelope budgeting sets the limits on the source side, and a pile of money approaching zero is very intuitive.

Goal budgeting sets the goals (or limits) on the destination side. And we don't usually enforce not going over.

In either system, the numbers we pick can be goals (go at least this far) or limits (don't go beyond this).

You can also mix them: envelope/source limits for some categories, goal/destination thresholds for others. -->

Calculate return on investment

A tutorial for the roi (Return On Investment) command.

Cash-only investments

Let's consider the easy case first, where your assets and your investment is the same single commodity (in this case, USD), and whenever value of your investment changes, you record the change manually, balancing it against equity:unrealized gains.

Lets say that we found an investment in Snake Oil that is promising to give us 10% annually:

2019-01-01 Investing in Snake Oil
  assets:cash  -$100
  investment:snake oil

2019-12-24 Recording the growth of Snake Oil
  investment:snake oil   = $110
  equity:unrealized gains

For now, basic computation of the rate of return, as well as IRR and TWR, gives us the expected 10%:

$ hledger roi -Y --inv investment --pnl "unrealized"
+---++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
|   ||      Begin |        End || Value (begin) | Cashflow | Value (end) | PnL ||    IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=====++========++============+==========+
| 1 || 2019-01-01 | 2019-12-31 ||             0 |     $100 |        $110 | $10 || 10.00% ||     10.00% |   10.00% |
+---++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+

However, lets say that shorty after investing in the Snake Oil we started to have second thoughts, so we prompty withdrew $90, leaving only $10 in. Before Christmas, though, we started to get the "fear of missing out", so we put the $90 back in. So for most of the year, our investment was just $10 dollars, and it gave us just $1 in return:

2019-01-01 Investing in Snake Oil
  assets:cash  -$100
  investment:snake oil

2019-01-02 Buyers remorse
  assets:cash  $90
  investment:snake oil
       
2019-12-30 Fear of missing out
  assets:cash  -$90
  investment:snake oil

2019-12-31 Recording the growth of Snake Oil
  investment:snake oil   = $101
  equity:unrealized gains

Now IRR and TWR are drastically different:

$ hledger roi -Y --inv investment --pnl "unrealized"
+---++------------+------------++---------------+----------+-------------+-----++-------++------------+----------+
|   ||      Begin |        End || Value (begin) | Cashflow | Value (end) | PnL ||   IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=====++=======++============+==========+
| 1 || 2019-01-01 | 2019-12-31 ||             0 |     $100 |        $101 |  $1 || 9.32% ||      1.00% |    1.00% |
+---++------------+------------++---------------+----------+-------------+-----++-------++------------+----------+

Here, IRR tells us that we made close to 10% on the $10 dollars that we had in the account most of the time. And TWR is ... just 1%? Why?

Based on the transactions in our journal, TWR "thinks" that we are buying back $90 worth of Snake Oil at the same price that it had at the beginning of the year, and then after that our $100 investment gets $1 increase in value, or 1% of $100. Let's take a closer look at what is happening here by asking for quarterly reports instead of annual:

$ hledger roi -Q --inv investment --pnl "unrealized"
+-------++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
|       ||      Begin |        End || Value (begin) | Cashflow | Value (end) | PnL ||    IRR || TWR/period | TWR/year |
+=======++============+============++===============+==========+=============+=====++========++============+==========+
|     1 || 2019-01-01 | 2019-03-31 ||             0 |      $10 |         $10 |   0 ||  0.00% ||      0.00% |    0.00% |
|     2 || 2019-04-01 | 2019-06-30 ||           $10 |        0 |         $10 |   0 ||  0.00% ||      0.00% |    0.00% |
|     3 || 2019-07-01 | 2019-09-30 ||           $10 |        0 |         $10 |   0 ||  0.00% ||      0.00% |    0.00% |
|     4 || 2019-10-01 | 2019-12-31 ||           $10 |      $90 |        $101 |  $1 || 37.80% ||      1.00% |    4.03% |
+-------++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
| Total || 2019-01-01 | 2019-12-31 ||             0 |     $100 |        $101 |  $1 ||  9.32% ||      1.00% |    1.00% |
+-------++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+

Now both IRR and TWR are thrown off by the fact that all of the growth for our investment happens in Q4 2019. Reported rates are annualized, that is IRR computation is still yielding 9.32% and TWR is still 1%, but these rates are computed over three month period instead of twelve, so in order to get an annual rate they should be multiplied by four!

Let's try to keep a better record of how Snake Oil grew in value:

2019-01-01 Investing in Snake Oil
  assets:cash  -$100
  investment:snake oil

2019-01-02 Buyers remorse
  assets:cash  $90
  investment:snake oil

2019-02-28 Recording the growth of Snake Oil
  investment:snake oil  
  equity:unrealized gains  -$0.25

2019-06-30 Recording the growth of Snake Oil
  investment:snake oil  
  equity:unrealized gains  -$0.25

2019-09-30 Recording the growth of Snake Oil
  investment:snake oil  
  equity:unrealized gains  -$0.25

2019-12-30 Fear of missing out
  assets:cash  -$90
  investment:snake oil

2019-12-31 Recording the growth of Snake Oil
  investment:snake oil
  equity:unrealized gains  -$0.25

Would our quarterly report look better now? Almost:

$ hledger roi -Q --inv investment --pnl "unrealized"
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
|       ||      Begin |        End || Value (begin) | Cashflow | Value (end) |   PnL ||    IRR || TWR/period | TWR/year |
+=======++============+============++===============+==========+=============+=======++========++============+==========+
|     1 || 2019-01-01 | 2019-03-31 ||             0 |   $10.00 |      $10.25 | $0.25 ||  9.53% ||      2.50% |   10.53% |
|     2 || 2019-04-01 | 2019-06-30 ||        $10.25 |        0 |      $10.50 | $0.25 || 10.15% ||      2.44% |   10.15% |
|     3 || 2019-07-01 | 2019-09-30 ||        $10.50 |        0 |      $10.75 | $0.25 ||  9.79% ||      2.38% |    9.78% |
|     4 || 2019-10-01 | 2019-12-31 ||        $10.75 |   $90.00 |     $101.00 | $0.25 ||  8.05% ||      0.25% |    1.00% |
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
| Total || 2019-01-01 | 2019-12-31 ||             0 |  $100.00 |     $101.00 | $1.00 ||  9.32% ||      7.77% |    7.77% |
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+

Something is still wrong with TWR computation for Q4, and if you have been paying attention you know what it is already: big $90 buy-back is recorded prior to the only transaction that captures the change of value of Snake Oil that happened in this time period. Lets combine transactions from 30th and 31st of Dec into one:

2019-12-30 Fear of missing out and growth of Snake Oil
  assets:cash  -$90
  investment:snake oil
  equity:unrealized gains  -$0.25

Now growth of investment properly affects its price at the time of buy-back:

$ hledger roi -Q --inv investment --pnl "unrealized"
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
|       ||      Begin |        End || Value (begin) | Cashflow | Value (end) |   PnL ||    IRR || TWR/period | TWR/year |
+=======++============+============++===============+==========+=============+=======++========++============+==========+
|     1 || 2019-01-01 | 2019-03-31 ||             0 |   $10.00 |      $10.25 | $0.25 ||  9.53% ||      2.50% |   10.53% |
|     2 || 2019-04-01 | 2019-06-30 ||        $10.25 |        0 |      $10.50 | $0.25 || 10.15% ||      2.44% |   10.15% |
|     3 || 2019-07-01 | 2019-09-30 ||        $10.50 |        0 |      $10.75 | $0.25 ||  9.79% ||      2.38% |    9.78% |
|     4 || 2019-10-01 | 2019-12-31 ||        $10.75 |   $90.00 |     $101.00 | $0.25 ||  8.05% ||      2.33% |    9.57% |
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
| Total || 2019-01-01 | 2019-12-31 ||             0 |  $100.00 |     $101.00 | $1.00 ||  9.32% ||     10.00% |   10.00% |
+-------++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+

And for annual report, TWR now reports the exact profitability of our investment:

$ hledger roi -Y --inv investment --pnl "unrealized"
+---++------------+------------++---------------+----------+-------------+-------++-------++------------+----------+
|   ||      Begin |        End || Value (begin) | Cashflow | Value (end) |   PnL ||   IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=======++=======++============+==========+
| 1 || 2019-01-01 | 2019-12-31 ||             0 |  $100.00 |     $101.00 | $1.00 || 9.32% ||     10.00% |   10.00% |
+---++------------+------------++---------------+----------+-------------+-------++-------++------------+----------+

Using commodities and prices

Let's redo the same Snake Oil example, but creating a special commodity to track amount of Snake Oil we have.

We will use SNKOIL as a commodity name, and will assume that 1 SNKOIL = $1 at the beginning of 2019.

As before, we start with a simple example where we invest in SNKOIL, and by the end of 2019 our investment growth by 10%.

2019-01-01 Investing in Snake Oil
  assets:cash
  investment:snake oil   100 SNKOIL @@ $100

; Recording the growth of Snake Oil
P 2019-12-24  SNKOIL $1.1

We need to tell roi that we are interested in the growth of value of our investment with --value=then switch, which forces it to use prices that were in effect at each moment in time that roi inspects for its computations:

$ hledger roi -Y --inv investment --pnl "unrealized" --value=then
+---++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
|   ||      Begin |        End || Value (begin) | Cashflow | Value (end) |   PnL ||    IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=======++========++============+==========+
| 1 || 2019-01-01 | 2019-12-31 ||             0 |   $100.0 |      $110.0 | $10.0 || 10.00% ||     10.00% |   10.00% |
+---++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+

Following the story from the previous example, lets say that shorty after investing in the Snake Oil we started to have second thoughts, so we prompty withdrew $90, leaving only $10 in. Before Christmas, though, we started to get the "fear of missing out", so we put the $90 back in. So for most of the year, our investment was just $10 dollars (or, rather 10 SNKOIL):

2019-01-01 Investing in Snake Oil
  assets:cash
  investment:snake oil   100 SNKOIL @@ $100

2019-01-02 Buyers remorse
  assets:cash
  investment:snake oil   -90 SNKOIL @@ $90

2019-12-23 Fear of missing out
  assets:cash
  investment:snake oil  90 SNKOIL @@ $90

; Recording the growth of Snake Oil
P 2019-12-24  SNKOIL $1.1

These numbers do not look correct:

$ hledger roi -Y --inv investment --pnl "unrealized" --value=then
+---++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+
|   ||      Begin |        End || Value (begin) | Cashflow | Value (end) |   PnL ||    IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=======++========++============+==========+
| 1 || 2019-01-01 | 2019-12-31 ||             0 |   $100.0 |      $110.0 | $10.0 || 83.66% ||     10.00% |   10.00% |
+---++------------+------------++---------------+----------+-------------+-------++--------++------------+----------+

That is because the "Fear of missing out" buy-back transaction is likely incorrect: based on the price in that transaction, it look like we are buying back $90 worth of Snake Oil at the same price that it had at the beginning of the year, and then after that our investment gets sudden increase in value. This completely throws off IRR computations.

Let's say that we kept a better record of SNKOIL prices and we can compute a more precise amount of SNKOIL we bough back at the end of the year:

2019-01-01 Investing in Snake Oil
 assets:cash
 investment:snake oil   100 SNKOIL @@ $100

2019-01-02 Buyers remorse
 assets:cash
 investment:snake oil   -90 SNKOIL @@ $90

; Recording the price of Snake Oil
P 2019-02-28 SNKOIL $1.025
P 2019-06-30 SNKOIL $1.05
P 2019-09-30 SNKOIL $1.075

2019-12-23 Fear of missing out
 assets:cash 
 investment:snake oil   83.72 SNKOIL @@ $90 ; $90/$1.075 = 83.72

; Recording the growth of Snake Oil
P 2019-12-24  SNKOIL $1.1

Now our IRR looks better:

$ hledger roi -Y --inv investment --pnl "unrealized" --value=then
+---++------------+------------++---------------+----------+-------------+--------++--------++------------+----------+
|   ||      Begin |        End || Value (begin) | Cashflow | Value (end) |    PnL ||    IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+========++========++============+==========+
| 1 || 2019-01-01 | 2019-12-31 ||             0 | $100.000 |    $103.092 | $3.092 || 25.22% ||      3.09% |    3.09% |
+---++------------+------------++---------------+----------+-------------+--------++--------++------------+----------+

Though from IRR perspective it looks like we only had about $10 worth of SNKOIL for most of the year, and still managed to get a return of $3, which looks like about a quarter of $10 invested - hence 25.22% of return from IRR standpoint. Note that TWR gives us more sensive 3.09% of return, which is closer to what you would expect, and shows that TWR is indeed (way) less sensitive to the money movements.

Investments that pay interest out (loans, bonds, dividends)

Let's say that you have given someone a loan or put money in a savings account, or maybe bought bonds that pay out regular coupons, and now you have monthly/quarterly/annual payouts. What is the best way to record them so that we could compute ROI?

For the following example, we will assume that you put $100 into savings account that pays out $1 quarterly (so your interest is not added to your investment):

2019-01-01 Investment
  assets:cash
  investment:saving  $100

We need to make sure that:

  • Payout transactions are included in the analysis, so they must match the query given to --inv. This could be achieved if investment account is mentioned in our transaction.

  • Payout transactions do not change the value of the investment

These two bullet points are naturally translated to this transaction:

2019-03-31 Interest
  assets:cash         $1
  investment:saving   $0

This transaction is not balanced, though. We need to balance it with the "profit and loss" account:

2019-03-31 Interest
  assets:cash         $1
  investment:saving   $0
  equity:profit and loss

So, at the end of the quarter your investment grew $1 in value and that $1 was immediately paid out to you, and this transaction lines with the description pretty well.

Let's complete the journal with one year of payouts:

2019-01-01 Investment
  assets:cash
  investment:saving  $100

2019-03-31 Q1 Interest
  assets:cash         $1
  investment:saving   $0
  equity:profit and loss

2019-06-30 Q2 Interest
  assets:cash         $1
  investment:saving   $0
  equity:profit and loss

2019-09-30 Q3 Interest
  assets:cash         $1
  investment:saving   $0
  equity:profit and loss

2019-12-31 Q4 Interest
  assets:cash         $1
  investment:saving   $0
  equity:profit and loss

We can now compute ROI:

$ hledger roi -Y --inv investment --pnl "profit and loss"
+---++------------+------------++---------------+----------+-------------+-----++-------++------------+----------+
|   ||      Begin |        End || Value (begin) | Cashflow | Value (end) | PnL ||   IRR || TWR/period | TWR/year |
+===++============+============++===============+==========+=============+=====++=======++============+==========+
| 1 || 2019-01-01 | 2019-12-31 ||             0 |      $96 |        $100 |  $4 || 4.06% ||      4.06% |    4.06% |
+---++------------+------------++---------------+----------+-------------+-----++-------++------------+----------+

Cashflow is $100 paid in minus $4 of interest received back.

Calculate unrealized gain

This is a guide on calculating the unrealized capital gain/loss of investments, using the balance --gain report (currently unreleased and available only in the git repo).

This guide assumes you've read the investments guide and that you're using the "simple" version of recording investment transactions laid out in that document, using @ or @@. We'll also be using a FIFO system for sales. At the end we'll discuss how to adapt your strategy for different systems.

Buying

Let's say you start your year with $100:

2021-01-04 opening balances
    assets:cash                      $100.00
    equity:opening/closing balances

In february you decide to start investing in a stock you've been monitoring:

P 2021-01-04 ABC $2
P 2021-01-11 ABC $3
P 2021-01-18 ABC $4

2021-01-19 buying stock
    assets:stocks:ABC:20210119   5 ABC @ $4.40
    assets:cash               $-22.00

Note that the stock was not bought at the exact market price we recorded.

Looking at the gain report at this point tells us that we've lost $2:

$ hledger bal --gain

              $-2.00  assets:stocks:ABC:20210119
--------------------
              $-2.00

This is because if we sell at the last known market price, that is what our loss will be. If we add --infer-market-price hledger will insert a P 2021-01-19 ABC $4.40 during processing, making the gain $0.

Price changes

Let's say the market price grows some more in the following weeks:

P 2021-01-25 ABC $5
P 2021-02-01 ABC $6

We now see a profit of $8:

$ hledger bal --gain

                  $8  assets:stocks:ABC:20210119
--------------------
                  $8

We can also track the gain over time:

$ hledger bal --gain -W

Incremental gain in 2021-01-04..2021-02-07, valued at period ends:

                            || 2021-01-04W01  2021-01-11W02  2021-01-18W03  2021-01-25W04  2021-02-01W05 
============================++===========================================================================
 assets:stocks:ABC:20210119 ||             0              0         $-2.00          $5.00          $5.00 
----------------------------++---------------------------------------------------------------------------
                            ||             0              0         $-2.00          $5.00          $5.00 

We start out with our gain of $-2 as before. Since the value of the stock increases by $1 every week and we bought 5 shares, we see a $5 increment in the following weeks. To see the total gain over time add the -H option.

At this point, you decide to buy some more shares:

2021-02-02 buying more stock
    assets:stocks:ABC:20210202   4 ABC @ $6.50
    assets:cash               $-26.00

Let's see what this does to our gain calculation:

$ heldger bal --gain
                  $8  assets:stocks:ABC:20210119
                 $-2  assets:stocks:ABC:20210202
--------------------
                  $6

We still see the same gain for the first lot. The new lot has a gain of $-2, decreasing our total gain to $6.

Unfortunately, in the next week the market price of the stock decreases:

P 2021-02-08 ABC $5

Our gain report over time now looks like this:

$ hledger bal --gain -W -b 2021-01-18

Incremental gain in 2021-01-04..2021-02-14, valued at period ends:

                            || 2021-01-18W03  2021-01-25W04  2021-02-01W05  2021-02-08W06 
============================++============================================================
 assets:stocks:ABC:20210119 ||        $-2.00          $5.00          $5.00         $-5.00 
 assets:stocks:ABC:20210202 ||             0              0         $-2.00         $-4.00 
----------------------------++------------------------------------------------------------
                            ||        $-2.00          $5.00          $3.00         $-9.00 

We'll add some more prices, and then start selling our stocks.

P 2021-02-15 ABC $6
P 2021-02-22 ABC $7

Selling

We'll start of by selling some of our first lot:

2021-02-23 sell some stock
    assets:cash                $12.00
    assets:stocks:ABC:20210119  -2 ABC @ $4.40
    income:capital gains

This leaves us with the following gain report:

$ hledger bal -W --gain -b 2021-02-07

Incremental gain in 2021-02, valued at period ends:

                            || 2021-02-01W05  2021-02-08W06  2021-02-15W07  2021-02-22W08 
============================++============================================================
 assets:stocks:ABC:20210119 ||         $5.00         $-5.00          $5.00         $-0.20 
 assets:stocks:ABC:20210202 ||        $-2.00         $-4.00          $4.00          $4.00 
----------------------------++------------------------------------------------------------
                            ||         $3.00         $-9.00          $9.00          $3.80 

At first glance, the negative value in the last column might seem counterintuitive. Didn't we just make a profit on our sale? However, our unrealized gain decreased by the sale. The realized gain is recorded by the income:capital gains posting. Our income statement tells us our realized gain:

$ hledger is

Income Statement 2021-01-04..2021-02-23

                      || 2021-01-04..2021-02-23 
======================++========================
 Revenues             ||                        
----------------------++------------------------
 income:capital gains ||                  $3.20 
----------------------++------------------------
                      ||                  $3.20 
======================++========================
 Expenses             ||                        
----------------------++------------------------
----------------------++------------------------
                      ||                        
======================++========================
 Net:                 ||                  $3.20 

The total value of the remaining 3 stocks we didn't sell increased by $3 that week, leaving us the $-0.20 figure we see in the last column of the gain report.

The next week, the price increases again, and we decide to sell all our remaining stock:

P 2021-03-01 ABC $8

2021-03-02 sell remaining stock
    assets:cash                $54.00
    assets:stocks:ABC:20210119  -3 ABC @ $4.40
    assets:stocks:ABC:20210202  -4 ABC @ $6.50
    income:capital gains

This decreases our remaining unrealized gain down to 0:

$ hledger bal -W --gain -b 2021-02-07 -H

Historical gain in 2021-02-01..2021-03-07, valued at period ends:

                            || 2021-02-07  2021-02-14  2021-02-21  2021-02-28  2021-03-07 
============================++============================================================
 assets:stocks:ABC:20210119 ||      $8.00       $3.00       $8.00       $7.80           0 
 assets:stocks:ABC:20210202 ||     $-2.00      $-6.00      $-2.00       $2.00           0 
----------------------------++------------------------------------------------------------
                            ||      $6.00      $-3.00       $6.00       $9.80           0 

Different cost base calculations

The transactions described above could easily be adapted for LIFO: just use a different lot for the initial sale.

ACB gets a bit more difficult. If we buy more stock, we essentially have to change the cost basis of our previous lots as well. The resulting journal might look a little like this:

2021-01-04 opening balances
    assets:cash                      $100.00
    equity:opening/closing balances

P 2021-01-04 ABC $2
P 2021-01-11 ABC $3
P 2021-01-18 ABC $4

2021-01-19 buying stock
    assets:stocks:ABC:20210119   5 ABC @ $4.40
    assets:cash               $-22.00

P 2021-01-25 ABC $5
P 2021-02-01 ABC $6

2021-02-02 buying more stock
    assets:stocks:ABC:20210119  -5 ABC @ $4.40
    assets:stocks:ABC:20210119   5 ABC @ $5.33
    assets:stocks:ABC:20210202   4 ABC @ $5.33
    assets:cash               $-26.00
    equity:rounding

P 2021-02-08 ABC $5
P 2021-02-15 ABC $6
P 2021-02-22 ABC $7

2021-02-23 sell some stock
    assets:cash                $12.00
    assets:stocks:ABC:20210119  -2 ABC @ $5.33
    income:capital gains

P 2021-03-01 ABC $8

2021-03-02 sell remaining stock
    assets:cash                $54.00
    assets:stocks:ABC:20210119  -3 ABC @ $5.33
    assets:stocks:ABC:20210202  -4 ABC @ $5.33
    income:capital gains

If and how you deal with rounding depends on if and how you need to report your capital gains to your tax authority. You can avoid rounding errors by always using the @@ symbol, but this essentially makes it impossible to sell only part of your shares (or you have to deal with rounding at time of sale instead of time of purchase). A journal might then look like this:

2021-01-04 opening balances
    assets:cash                      $100.00
    equity:opening/closing balances

P 2021-01-04 ABC $2
P 2021-01-11 ABC $3
P 2021-01-18 ABC $4

2021-01-19 buying stock
    assets:stocks:ABC   5 ABC @@ $22.00
    assets:cash      $-22.00

P 2021-01-25 ABC $5
P 2021-02-01 ABC $6

2021-02-02 buying more stock
    assets:stocks:ABC  -5 ABC @@ $22.00
    assets:stocks:ABC   9 ABC @@ $48.00
    assets:cash      $-26.00

P 2021-02-08 ABC $5
P 2021-02-15 ABC $6
P 2021-02-22 ABC $7
P 2021-03-01 ABC $8

2021-03-02 sell stock
    assets:cash                $70.00
    assets:stocks:ABC:20210119  -9 ABC @@ $48.00
    income:capital gains

Change account name separator

Timedot format makes me want to use dots (.) for separating account components, instead of colon (:). For example, instead of fos:hledger:timedot I'd like to write fos.hledger.timedot. We can use the powerful account aliases feature to rewrite account names before hledger's account name parser sees them.

In journal files, we can use an alias directive. Note the backslash which tells the regular expression engine it's a literal . not a wildcard:

# alias /REGEX/=REPLACEMENT
alias /\./=:

2008/01/01 income
    assets.bank.checking  $1
    income.salary

Check that subaccounts are recognised:

$ hledger -f t.journal bal --no-elide
                  $1  assets
                  $1    bank
                  $1      checking
                 $-1  income
                 $-1    salary
--------------------
                   0

Alias directives aren't supported in the timedot format,

2016/2/4
fos.hledger.timedot  2
fos.ledger           1

so we would use the --alias command line option instead. The second backslash tells the shell that's a literal backslash, not a shell escape sequence:

$ hledger --alias /\\./=: -f t.timedot bal --no-elide
                3.00  fos
                2.00    hledger
                2.00      timedot
                1.00    ledger
--------------------
                3.00

Charts and Graphs

Tips and techniques for producing graphical charts from hledger data.

The most common general approach is to produce simple CSV output from a report - usually the balance report with -N/--no-total and --layout=bare:

hledger bal expenses -N --layout bare -o report.csv

and then use one of the many ways to make charts from CSV data.

Charting tools built for hledger

Simplest first:

hledger-bar

hledger-bar (2023) is a bash script for making quick bar charts in the terminal.

$ hledger bar someacct
2023-01	+++++
2023-02	++++
2023-03	++++
2023-04	+
$ hledger bar -- -v 1 -f $TIMELOG biz
2023-01	        15 +++++++++++++++
2023-02	        10 ++++++++++
2023-03	        20 ++++++++++++++++++++
2023-04	        12 ++++++++++++
$ hledger bar -- -v 1 -f $TIMELOG biz -p weeklyfrom3weeksago
2023-03-27W13	         8 ++++++++
2023-04-03W14	         2 ++
2023-04-10W15	         4 ++++
2023-04-17W16	         5 +++++

hledger-plot

hledger-plot (2023) is a powerful graphical chart-making tool written in python.

hledger-sankey

hledger-vega

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

hledger-vega example

r-ledger

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

Other tools

Spreadsheets

Drag the CSV file into your favourite spreadsheet app and use its interactive charting tools.

Ledger chart tools

Tools built for Ledger or other PTA apps can sometimes be adapted to work with hledger also; or, hledger data can be exported to be read by Ledger.

ploterific

ploterific (stack install hvega-theme ploterific) produces simple charts, in a HTML file that uses the Vega-Lite javascript library. Charts can also be saved as SVG or PNG. An example:

hledger -f examples/bcexample.hledger bal -O csv -N expenses -3 cur:USD \
    | sed 's/ USD//' \
    | ploterific -m Bar -f account:N -f balance:Q -c account -o a.html \
    && open a.html

ploterific example 1

Let's break down that command line:

  • -f examples/bcexample.hledger - use this example file in the hledger repo. Omit this to use your default journal.
  • bal - run a balance report
  • -O csv - show it as CSV on stdout
  • -N - disable the final Total row
  • expenses - limit to accounts whose name contains expenses
  • -3 - summarise accounts to depth 3 and above
  • cur:USD - limit to balances in USD currency. If you use the $ symbol, it would be cur:\\$.
  • sed 's/ USD//g' - process the output with sed, stripping the USD symbols to leave bare numbers for ploterific. With $ it would be sed 's/\$//g'.
  • -m Bar - use Bar as the Vega-Lite mark type
  • -f account:N - use the account column as the first feature (X axis), treating values as names
  • -f balance:Q - use the balance column as a second feature (Y axis), treating values as quantities
  • -c account - use account values to select colours
  • -o a.html - save into a temporary HTML file
  • && open a.html - and view it in your web browser, on Mac; on other systems it might be xdg-open or start

Here is the same chart but with the colour set by the balance:

hledger -f examples/bcexample.hledger bal -O csv -N expenses -3 cur:USD \
    | sed 's/ USD//' \
    | ploterific -m Bar -f account:N -f balance:Q -c balance:Q -o a.html

ploterific example 2

SankeyMATIC

The format is Source [Amount] Target. A rough script that exports the outflows from several asset accounts:

LEDGER_FILE=examples/sample.journal
for f in checking saving cash; do 
  hledger areg $f -O tsv | tail +2 | sed -e "s/^/$f\t/"
done | cut -f1,6,7 | gsed -E -e 's/\$//' -e 's/([^\t]*)\t([^\t]*)\t([^\t]*)/\1 [\3] \2/' | grep '\[-' | gsed 's/\[-/[/'

Checking for errors

hledger can check your data in various ways.

Built in checks

In hledger 1.21+, see strict mode and the check command.

Old way to check accounts

Here's another way to check for undeclared accounts, that works with older hledger versions, showing some diff tricks:

$ diff -U0 --label "Unused Accounts" --label "Undeclared Accounts" <(hledger accounts --declared) <(hledger accounts --used)

Compare report output

Save the output of a report, and later use diff to compare the output of the same report, revealing any changes.

$ hledger COMMAND > report.txt
$ hledger COMMAND > report2.txt
$ diff report.txt report2.txt

Or, periodically commit a report's output into your version control system, then use the VCS to detect any changes since the last commit.

$ hledger COMMAND > report.txt; git add report.txt; git commit -m 'report' report.txt
$ hledger COMMAND > report.txt; git diff -- report.txt

Sometimes new data can cause noisy diffs, especially in tabular reports. To minimise diffs, here are some things to try:

When comparing [balancesheetequity] diffs, do any of these help:

  • --layout wide,MAXWIDTH, --layout bare, --layout tidy

  • not using tree mode if you're using that

  • not showing multiple periods if you're using that

  • overriding commodity styles with -c, eg limiting precision

  • limiting --depth

  • -MA --summary

  • excluding particular problem accounts/commodities

  • generating one report per commodity with cur: ?

  • generating csv/tsv/json output ?

  • using the balance command instead, and eg --transpose, or a single --period and --format

  • using the [a]register command

A pre-commit hook

Version control systems often support a "pre-commit hook", a script which is required to succeed before each commit. Eg:

#!/bin/bash
set -e
hledger check -s

Flycheck mode

If you use Emacs, you can configure flycheck to run your preferred checks when you edit a journal file. This integration is currently quite basic, but it still gives very useful real-time feedback. Setup tips:

  • in Emacs, install the flycheck-hledger package and customise the flycheck-checkers variable, adding hledger to the list
  • customise the flycheck-hledger-strict and flycheck-hledger-checks variables

Todo / maybe

Here are some checks we don't support, but could:

  • accountsactive - for each account used, if there is posting with an open: tag, it must have a corresponding posting with a close: tag, and all other postings must be chronologically between (and if on the same date, textually between) open and close postings. ("Accounts are posted to only within their declared active period.")
  • explicitamounts - all transaction amounts have been recorded explicitly

Preventing postings to closed accounts

Some options (from https://www.reddit.com/r/plaintextaccounting/comments/180a11b/how_to_represent_closure_of_an_account_using):

  1. Add a balance assertion when the account is closed, and then use hledger check recentassertions (https://hledger.org/1.31/hledger.html#other-checks). After a week has passed, new postings to it (without a new, correct, balance assertion) will raise an error.

  2. Add a zero balance assertion in the far future. Eg:

    12000-01-01 * This balance shall remain zero for ten thousand years assets:foo2022 0 ==* 0

  3. (If yearly granularity is ok:) Use a new journal file and new account declarations, and drop the obsolete accounts, each year. Strict mode or hledger check accounts will warn of any new references to the old account.

  4. (If you are comfortable with unix tools:) Make a custom check script, eg grep the output of hledger reg OLDACCT for inappropriate dates.

  5. https://hledger.org/scripts.html#hledger-check-fancyassertions might help.

  6. support account lifetimes ? #1389 is the place to work on better support for this.

Climate impact

Most human endeavours should maintain and publish an environmental cost/benefit report. "Cost" means some environmental impact such as resources used (energy, water, land..)
or undesirable waste generated (carbon dioxide, toxins, heat, noise, harm..)

Here is the beginnings of one for the hledger project, with a catchier name.

Development

Costs related to developing hledger come from:

  • developer machine activity
    • recompiling, linking, building docs, browsing, running IDEs etc.
  • test machine activity
    • CI workers recompiling on push, on PR updates etc.
  • source code hosting
    • VCS operations, file and artifact hosting, rich web UIs, issue tracking, forks, notifications (github etc.)
  • public websites and services
    • hledger.org doc hosting, updating
    • demo.hledger.org hosting
    • discussions (matrix, irc, reddit, hacker news..)
    • media (youtube..)
    • project analysis (libhunt..)

Benefits:

  • providing effective accounting tools to people and organisations who would be less effective/less empowered by other tools, potentially unlocking further eco accounting activity and successes
  • potentially help advance best practices and community know-how by sharing useful techniques, tools, processes, architecture, patterns

Delivery

Costs related to packaging and shipping hledger to end-users. Related to development, but broken out separately here. This includes:

  • Github artifact building / additional testing beyond standard dev tests
  • Hackage hosting/maintenance
  • Stackage hosting/maintenance
  • Hosting/maintenance in all the other packaging systems and distributions on the Install page
  • Download/install/cloning/build work done by user machines

Benefits:

  • Roughly speaking, more work done on packaging and delivery means more users able to try out and benefit from the software, magnifying the Development benefits above

Your hledger usage

Costs from your individual/organisational usage of hledger, other than Delivery costs above, may include:

  • Machine usage every time you run hledger
  • Hardware upgrade/purchase pressure to satisfy memory/disk space requirements
  • Personal and online machine usage and human time while learning, discussing, configuring and maintaining hledger and PTA

Benefits:

  • Hopefully, more effective accounting and eco accounting, reducing waste and increasing personal/organisational solvency, success, empowerment and positive impact

Stats

Some useful statistics to guestimate:

  • Yearly cost
  • Yearly cost trend
  • Cumulative cost
  • Predicted lifetime cost
  • Comparison with alternate solutions

Command line completion

Command-line completion is a feature in shells (Bash, Fish, Zsh, ...) to automatically complete a command, argument, or option. Usually, the completion is triggered by pressing the tab key once or twice after typing hledger . (The exact behavior may differ in shells other than Bash.)

asciicast

The completions handle hledger's CLI:

  • commands and generic options
  • command-specific options
  • filenames for options that take a filename as argument
  • account names from journal files (but not yet for files named by --file)
  • query filter keywords like status:, tag:, or amt:

Installation for end users

Completions are currently only implemented for the Bash shell.

Please check first if the completions for hledger are already installed on your distribution. Refer to the last paragraph of this section for how to test that.

To install the completions manually, follow this steps:

  • Download or copy the file hledger/shell-completion/hledger-completion.bash and save it as ~/.hledger-completion.bash.

    Note: Prior to version 1.25, the shell-completion directory was at the repository root (not hledger/shell-completion). Update the URLs, taking this into account, if you are trying to download the completion script for an older version of hledger such as 1.21.

  • Add the command source ~/.hledger-completion.bash to the end of your ~/.bashrc file.

  • Then, you have to start a new Bash, e.g. by typing bash on the current shell.

Example installation script:

curl https://raw.githubusercontent.com/simonmichael/hledger/master/hledger/shell-completion/hledger-completion.bash > ~/.hledger-completion.bash
echo 'source ~/.hledger-completion.bash' >> ~/.bashrc
bash  # open a new bash to try it

Now, try it by typing hledger (with a space after the command) and press the tab key twice. You should see a list of appropriate completions for hledger. Then you can type a part of one of the suggestions and press tab again to complete it. If you only see filenames, the completions are not correctly installed.

Completion scripts for other shells

You're welcome to add completion scripts for other shells (e.g. Fish or Zsh)! It should not be too hard. All available hledger options and commands are already there. Only the shell hooks and logic is missing.

Please refer to the README in the shell-completion folder.

Common journal entries

Here are entries for some common transactions. Check other pages, or https://wiki.plaintextaccounting.org for more detailed examples.

Shopping

2017/1/26 market
  expenses:food    $10
  assets:cash

Invoicing, accrual basis

2018-04-16 * (2018-001) SuperCompany invoice
    Revenue:Software Development                        $ -2420.00
    Assets:Receivable:SuperCompany                       $ 2420.00

2018-04-26 * (2018-001) SuperCompany payment
    Assets:Receivable:SuperCompany                      $ -2420.00 = $0
    Assets:Checking                                      $ 2420.00

Invoicing, cash basis

; Invoices aren't tracked in cash basis, use unbalanced postings to track them anyway.

2018-04-16 * (2018-001) SuperCompany invoice
    (Assets:Receivable:SuperCompany)                      $2420

2018-04-26 * (2018-001) SuperCompany payment
    (Assets:Receivable:SuperCompany)                     $-2420 = $0
    Revenue:Software Development                         $-2420
    (Liabilities:Tax:2018)                                $-420
    Assets:Checking:Estimated Tax Savings:2018             $420
    Assets:Checking                                       $2000

The above plus postings to track and save estimated income tax:

2018-04-26 * (2018-001) SuperCompany payment
    (Assets:Receivable:SuperCompany)                     $-2420 = $0
    Revenue:Software Development                         $-2420
    (Liabilities:Tax:US:2018)                             $-420
    Assets:Checking:Tax:US:2018                            $420
    Assets:Checking                                       $2000

Tracking a mortgage

2019/01/01 Buy House
    Assets:House                                      500,000.00
    Liabilities:Mortgage

2019/02/01 Mortgage Payment
    Liabilities:Mortgage                                1,000.00
    Expenses:Interest:Real Estate                         833.33
    Assets:Cash                                         -1833.33

2019/03/01 Mortgage Payment
    Liabilities:Mortgage                                1,002.00
    Expenses:Interest:Real Estate                         831.33
    Assets:Cash                                         -1833.33

2019/03/01 Zillow Price Estimate
    Assets:House                                                 = 505,000.00  ; assign new balance, generating a transaction
    Equity:Unrealized Gains

Workflows

There are lots of ways to use hledger; here is an overview. Of course you can mix and match these. Also remember always to back up your files regularly to safeguard your data.

Basic workflows

Command line

At a terminal prompt, run hledger add and follow the interactive prompts to enter transactions. It will store data in its default location. Run hledger to list commands to try. Eg, run hledger bs to see your account balances (a balance sheet), and hledger is to see your income and expenses (an income statement).

Tutorial: hledger basics describes this process, and hledger's basic concepts and file format, step by step. You might want to skim through this one even if you don't plan to use hledger add.

Web UI

This is the easiest hledger workflow, if it works for you:

Download and run hledger-web, eg by double-clicking on it. It should open in your web browser. Use the add form to add transactions. It will store data in its default location. (So you'll see your transactions next time you run it.)

Tutorial: hledger-web describes this in more detail.

Terminal UI

Use hledger add once to create a journal file with an opening balances transaction. Now run hledger-ui to view account balances. Use the onscreen help to get around and discover the controls. Eg, press a to add new transactions.

Tutorial: hledger-ui describes this setup in more detail.

Text editor

Open your preferred text editor and create a journal file, either .hledger.journal in your home directory, or somewhere else like ~/finance/2023.journal (and set the LEDGER_FILE environment variable). Create transactions by hand using journal file format. Once you have a few, you can copy/paste them to make more. When you want more assistance, set up an editor mode. Here's an example:

; $HOME/.hledger.journal

2020-01-01 opening balances
    assets:checking         $1234
    equity

2020-03-15 client payment
    assets:checking         $2000
    income:consulting

2020-03-20 Sprouts
    expenses:food:groceries  $100
    assets:cash               $40
    assets:checking

Run hledger in a terminal to see reports, as in the Command line workflow. Eg:

$ hledger bs
Balance Sheet 2020-03-20

             || 2020-03-20 
=============++============
 Assets      ||            
-------------++------------
 assets      ||      $3134 
   cash      ||        $40 
   checking  ||      $3094 
-------------++------------
             ||      $3134 
=============++============
 Liabilities ||            
-------------++------------
-------------++------------
             ||            
=============++============
 Net:        ||      $3134 

$ hledger is -M
Income Statement 2020-01-01-2020-03-20

                         || Jan  Feb    Mar 
=========================++=================
 Revenues                ||                 
-------------------------++-----------------
 income:consulting       ||   0    0  $2000 
-------------------------++-----------------
                         ||   0    0  $2000 
=========================++=================
 Expenses                ||                 
-------------------------++-----------------
 expenses:food:groceries ||   0    0   $100 
-------------------------++-----------------
                         ||   0    0   $100 
=========================++=================
 Net:                    ||   0    0  $1900 

CSV import

Download CSV files from banks and financial institutions, manually or using tools/services that automate this (ledger_autosync, Plaid, plaid2qif, Tiller etc.) Use hledger's import command to convert and import the new transactions, and use any of the hledger UIs to see reports.

Importing CSV data is a quick tutorial on the importing from CSV part. Some downloading helpers can be found at https://plaintextaccounting.org/#data-importconversion (search for "download").

More advanced workflows

hledgermatic

SM's time & task dashboard

hledger-flow

Full-fledged hledger

rtrledger

barrucadu's setup

SM's old setup

Cost notation

Here is a little more background on cost notations, moved here from hledger manual > Cost reporting.

Essentially there are four ways to record a commodity conversion in hledger (as of 2023):

Conversion with implicit cost

Let's assume 100 EUR is converted to 120 USD. You can just record the outflow (100 EUR) and inflow (120 USD) in the appropriate asset account:

2021-01-01
    assets:cash    -100 EUR
    assets:cash     120 USD

hledger will assume this transaction is balanced, inferring that the conversion rate must be 1 EUR = 1.20 USD. You can see the inferred rate by using hledger print -x.

Pro:

  • Concise, easy

Con:

  • Less error checking - typos in amounts or commodity symbols may not be detected
  • Conversion rate is not clear
  • Disturbs the accounting equation, unless you add the --infer-equity flag

You can prevent accidental implicit conversions due to a mistyped commodity symbol, by using hledger check commodities.

You can prevent implicit conversions entirely, by using hledger check balancednoautoconversion, or -s/--strict.

Conversion with explicit cost

You can add the conversion rate using @ notation:

2021-01-01
    assets:cash        -100 EUR @ 1.20 USD
    assets:cash         120 USD

Now hledger will check that 100 * 1.20 = 120, and would report an error otherwise.

Pro:

  • Still concise
  • Makes the conversion rate clear
  • Provides more error checking

Con:

  • Disturbs the accounting equation, unless you add the --infer-equity flag

Conversion with equity postings

In strict double entry bookkeeping, the above transaction is not balanced in EUR or in USD, since some EUR disappears, and some USD appears. This violates the accounting equation (A+L+E=0), and prevents reports like balancesheetequity from showing a zero total.

The proper way to make it balance is to add a balancing posting for each commodity, using an equity account:

2021-01-01
    assets:cash        -100 EUR
    equity:conversion   100 EUR
    equity:conversion  -120 USD
    assets:cash         120 USD

Pro:

  • Preserves the accounting equation
  • Keeps track of conversions and related gains/losses in one place
  • Standard, works in any double entry accounting system

Con:

  • More verbose
  • Conversion rate is not obvious
  • Cost reporting requires adding the --infer-costs flag

Conversion with equity postings and explicit cost

Here both equity postings and @ notation are used together.

2021-01-01
    assets:cash        -100 EUR @ 1.20 USD
    equity:conversion   100 EUR
    equity:conversion  -120 USD
    assets:cash         120 USD

Pro:

  • Preserves the accounting equation
  • Keeps track of conversions and related gains/losses in one place
  • Makes the conversion rate clear
  • Provides more error checking

Con:

  • Most verbose
  • Not compatible with ledger

Create a journal

See also: Input and Setting LEDGER_FILE and Starting a journal file in the hledger manual.

There are lots of ways to start and update a journal file:

with touch

The simplest possible journal is just an empty file:

$ touch 2023.journal

The name doesn't matter much and can be changed later. One file per year is common, and so is a .journal or .hledger extension.

with cat

$ cat >>2023.journal
2023-01-26
    expenses:food     $10
    assets:cash
<CTRL-D>

Account names can be anything and you can change them later by search and replace. If you don't know what to choose, start with these five:
expenses, income, assets, liabilities, and equity,
perhaps with one extra subcategory as above.

with a text editor

Write transactions in a text editor, optionally using an editor mode, and save the file.

with hledger add

Use the interactive add command and follow the prompts:

$ hledger add

with hledger-iadd

  • ensure $LEDGER_FILE exists
  • hledger iadd
  • enter one or more transactions

with hledger-web

  • ensure $LEDGER_FILE exists
  • hledger web
  • wait for web browser to open
  • click "add transaction" or press "a"
  • enter a transaction, click ok or press enter

Currency conversion

Here are various ways of recording a conversion from one currency or commodity to another.

Implicit conversion

For simplicity, let's assume we are just exchanging cash with a friend:

2021-07-27 give dollars, get euros
    assets:cash      USD -10.00
    assets:cash      EUR   8.50

hledger understands that 10 dollars were converted to 8.50 euros. A conversion rate is inferred automatically so as to make the transaction balance. (This can be seen with hledger print -x.)

This is easy to write and to understand; it's fine for getting started. However it is not a fully correct double-entry-bookkeeping journal entry, since USD has magically transformed into EUR "in flight". It is also somewhat error prone, since a typo in either amount may not be detected. For example, we might forget the decimal point and write USD -1000. Also, it is easy to create such entries accidentally. For example, in one posting within a transaction we might mistype or omit the currency symbol.

hledger accepts these implicit conversions by default, for convenience and compatibility. But you can disallow them by using strict mode or by running the check command (eg: hledger check balancednoautoconversion).

Declared conversion rate

We can declare the conversion rate, which adds redundancy allowing hledger to catch errors, and also makes the rate more explicit to human readers. We can write the total amount with @@ (convenient when entries are complex):

2021-07-27 give dollars, get euros
    assets:cash      USD -10.00 @@ EUR 8.50
    assets:cash      EUR   8.50

or the per-unit amount with @ (makes the exchange rate, or an investment's cost basis, clearer):

2021-07-27 give dollars, get euros
    assets:cash      USD -10.00 @ EUR 0.85
    assets:cash      EUR   8.50

hledger calls these "costs". They can also be used generate market prices for value reports.

This is probably the most frequently used style among hledger users.

Fully balanced conversion

A fully correct double-entry-bookkeeping journal entry avoids the PTA-specific @/@@ notation, and is balanced in each commodity, using equity. Eg:

2021-07-27 give dollars, get euros
    assets:cash        USD -10.00
    equity:conversion  USD  10.00
    assets:cash        EUR   8.50
    equity:conversion  EUR  -8.50

or, letting hledger infer the above:

2021-07-27 give dollars, get euros
    assets:cash        USD -10.00
    assets:cash        EUR   8.50
    equity:conversion

This is discussed more here.

Two entries

This comes up eg when converting Paypal CSV, which provides two records for a currency conversion, one for each side. For example, given some paypal CSV rules like:

account1 assets:paypal
...
if %type (T0200|General Currency Conversion)
 description %type for %referencetxnid %itemtitle
 account2 equity:conversion

we might see journal entries like:

2020-05-16 * Liberapay donation to simonmichael (team darcs-hub)
    assets:online:paypal                     C$24.56 =* C$24.56
    revenues:foss donations:darcshub        C$-26.00
    expenses:banking:paypal                   C$1.44

2020-05-16 * General Currency Conversion for 8Y4N723T948333034 Liberapay donation to simonmichael (team darcs-hub)
    assets:online:paypal                    C$-24.56 =* C$0.00
    equity:conversion                        C$24.56

2020-05-16 * General Currency Conversion for 8Y4N723T948333034 Liberapay donation to simonmichael (team darcs-hub)
    assets:online:paypal                      $16.88 =* $17.55
    equity:conversion                        $-16.88

Ie: some canadian dollars received, followed by two transactions converting that balance to US dollars. This is equivalent to the "Fully balanced conversion" above, just with the conversion entry split into two.

Balancing the books

If you are a fan of the accounting equation and like to check it by seeing a zero total in the balancesheetequity report, you will need to do something with these equity:conversion balances. Such as, converting them to retained revenues/expenses in your local currency. This can perhaps be done at transaction time, or at the end of each accounting period, or with report options. Best practice is not yet clear, suggestions welcome.

Currency conversion 2

More notes related to currency conversion.

In a currency conversion or a stock purchase/sale, one commodity is exchanged for another. In plain text accounting, there are two ways to record such conversions:

1. Equity method

Balance both commodities against an Equity account. Eg:

2021-01-01
  assets:usd                -1.20 USD
  equity:conversion          1.20 USD
  equity:conversion         -1.00 EUR
  assets:eur                 1.00 EUR

or, equivalently:

2021-01-01
  assets:usd                -1.20 USD
  assets:eur                 1.00 EUR
  equity:conversion

2. Conversion price method

PTA tools provide the @ (or @@) notation for specifying a conversion price (essentially; Ledger/Beancount also provide an alternate {} notation):

2021-01-01
  assets:usd                -1.20 USD
  assets:eur                 1.00 EUR @ 1.20 USD

@-priced amounts (the 1.00 EUR above) will be converted to their price's commodity (USD)

  • internally for checking transaction balancedness, always
  • and visibly in reports, when the -B/--cost flag is used.

Note the redundancy in this entry; the two amounts and the @ price must agree. This provides some extra error checking, but you can also write it non-redundantly, by omitting an amount:

2021-01-01
  assets:usd                           ; the -1.20 USD amount is inferred
  assets:eur                 1.00 EUR @ 1.20 USD

or the conversion price:

2021-01-01
  assets:usd                -1.20 USD
  assets:eur                 1.00 EUR  ; the @ 1.20 USD price is inferred

Pros and Cons

Cost reporting

The @ (conversion price) method allows "cost" reporting. By adding the -B/--cost flag you can easily see what things cost (or were sold for) in the other commodity. Eg:

$ hledger -f 2a.j bal --cost assets:eur
            1.20 USD  assets:eur
--------------------
            1.20 USD  

This kind of report is not possible with the equity method, currently.

Gain/loss reporting

The equity method keeps a trace of all commodity exchanges in the equity account, in effect properly recording the accumulated gain/loss from all commodity exchanges (it can be seen by valuing the accumulated total of those equity balances in some commodity).

The @ method does not record the gain/loss from commodity exchanges (at least, not so explicitly and not grouped by commodity pair. We can still calculate it using hledger valuation features like -V, --valuechange, --gain.)

Balanced accounts

The equity method keeps accounts and the accounting equation (A+L+E=0) balanced. See how it keeps the balance report's total as zero:

$ hledger -f 1a.j bal
            1.00 EUR  assets:eur
           -1.20 USD  assets:usd
           -1.00 EUR
            1.20 USD  equity:conversion
--------------------
                   0  

The @ method causes unbalanced accounts and a non-zero total (because of the "magical" transformation from one commodity to the other):

$ hledger -f 2a.j bal
            1.00 EUR  assets:eur
           -1.20 USD  assets:usd
--------------------
            1.00 EUR
           -1.20 USD  

The zero total can be seen only if all amounts are converted to cost:

$ hledger -f 2a.j bal --cost
            1.20 USD  assets:eur
           -1.20 USD  assets:usd
--------------------
                   0  

Summary

The equity method:

  • doesn't support cost reporting.

The @ method:

  • doesn't support easy gain/loss reporting by commodity pair.
  • doesn't maintain balanced accounts

The rest of this page is about future versions of hledger.

Improvement proposals

The two methods of recording conversions were discussed mostly at https://github.com/simonmichael/hledger/issues/1177 in 2020.

1554

https://github.com/simonmichael/hledger/pull/1554 was proposed in 2021. Here's an attempted summary:

Goals / problems tackled

  1. Allow entries written with the @ style to be converted on the fly to equity style when appropriate.
  2. Allow all three of cost reporting, gain/loss reporting, and balanced accounts.
  3. Reduce required data entry effort.

Current draft docs

# COSTING

The --cost=TYPE option and the -B flag control how hledger handles any costs which are specified.

-B / --cost / --cost=cost : Convert amounts to their cost or sale amount at transaction time.

--cost=conversion : Generate conversion postings to balance the transactions. This is the default for all reports except the print report.

--cost=nocost : Do no conversion of costs. This is the default for the print report.

When performing cost conversion and price valuation, hledger will always perform cost conversion first, and market price valuations afterwards.

Sample tests

# 3. --cost=conversion generates conversion postings
hledger -f- print --explicit --cost=conversion
<<<
2011/01/01
    expenses:foreign currency       €100 @ $1.35
    assets
>>>
2011-01-01
    expenses:foreign currency            €100
    equity:conversion:€:$               €-100  ; generated-posting:
    equity:conversion:$:€             $135.00  ; generated-posting:
    assets                           $-135.00

>>>=0

# 4. --cost=conversion with --show-costs continues to show transaction costs
hledger -f- print --explicit --cost=conversion --show-costs
<<<
2011/01/01
    expenses:foreign currency       €100 @ $1.35
    assets
>>>
2011-01-01
    expenses:foreign currency    €100 @ $1.35
    equity:conversion:€:$               €-100  ; generated-posting:
    equity:conversion:$:€             $135.00  ; generated-posting:
    assets                           $-135.00

>>>=0

User-visible changes

  1. The -B/--cost flag becomes a flag -B which works as before, and an optional-argument option --cost[=nocost|cost|conversion]:
  • --cost or --cost=cost: works like -B (@-priced amounts are converted to cost)
  • --cost=conversion: in each @-style entries with no equity postings, adds two equity postings of the form:
    equity:conversion:FIRSTCOMM:SECONDCOMM    FIRSTCOMMAMT
    equity:conversion:FIRSTCOMM:SECONDCOMM   -SECONDCOMMAMT
    
    They are added dynamically (transiently), at report time. They are allowed to coexist with the @ price without unbalancing the transaction (which they would do if added explicitly by the user).
  • --cost=nocost: does neither of the above (ie, nothing)
  1. --cost=conversion will be the default behaviour of all commands except print.

Interactions / impact / compatibility

  1. Commands using -B or --cost (with no argument) should work as before.

  2. In conversion mode, all reports should work as they normally would with equity style entries.

  3. To mimic previous hledger behaviour (don't add equity postings to commodity conversions), users will need to add --cost=nocost (or, not:equity:conversion) to commands. This can be seen in the many changes required to hledger's tests.

  4. Except with print. print will have different default behaviour from all other commands.

Open questions

  • Want to avoid hard-coded "equity:conversion"

  • Why the :FIRSTCOMM:SECONDCOMM subaccounts, are they worth it ?

  • What journal entry variations are handled ?

    • a commodity conversion with other unrelated postings in the transactions
    • one commodity conversion involving more than two postings ?
    • more than one commodity conversions in a transaction ?
  • Why is the new feature (conversion) integrated with the existing --cost option ? Because they are closely related, and the combination of cost reporting and equity postings is not supported (and not expected in future ?)

  • What other names, or other changes, could make this more clear and mnemonic ?

  • When should the new mode be made default behaviour ?

  • Why is print different, and is it worth it ?

1554-sm-2

Goals / problems tackled

  1. Meet the goals of #1554 in a clearer and more compatible way.

Differences from 1554

  • -B/--cost and default behaviour are not changed
  • A new flag is added
  • The equity conversion account is configurable
  • The subaccounts are :FROM:TO, not :FIRST:SECOND.

Current draft docs

     --infer-equity          in commodity conversion transactions which lack
                             equity postings and rely on @/@@ prices to balance,
                             add the missing equity postings.

User-visible changes

  • The --infer-equity flag is added (consistent with --infer-market-prices). It is off by default. In a future release it would probably be on by default and there would probably be a --no-infer-equity to disable it.

  • From a conversion from FROMCOMM to TOCOMM, generated equity postings will have the form:

    EQUITYACCT:FROMCOMM:TOCOMM       -TOCOMMAMT
    EQUITYACCT:TOCOMM:FROMCOMM      FROMCOMMAMT
    
    • FROMCOMMAMT is the negative amount, TOCOMMAMT is the positive one
    • FROMCOMM, TOCOMM are the corresponding commodity symbols
    • EQUITYACCT is the first declared subaccount of the first highest-level account declared with type Equity, falling back to equity:conversion. So account declarations might be:
      account equity         ; type:E
      account equity:trades
      account equity:opening balances
      etc..
      

    Or, they might be of this simpler form, it's not yet decided:

    EQUITYACCT:COMMPAIR       -TOCOMMAMT
    EQUITYACCT:COMMPAIR      FROMCOMMAMT
    
    • COMMPAIR is the two commodity symbols concatenated in alphabetic order
  • In infer equity mode, conversion prices should be used only to infer equity postings, and otherwise should not be used for transaction balancing. This means that fully explicit entries with both equity postings and conversion prices recorded are supported, whether manually recorded or inferred.

Interactions / impact / compatibility

  • There is no change to default behaviour at this stage.

  • With --infer-equity, --cost reports should work as before, using @ conversion prices when they are present. Other reports should work as if the equity postings had been recorded manually.

Open questions

  • What's a better way to specify the conversion account(s) ? Should there be a new Conversion or Trade account type, a subtype of Equity, and the first account declared with that type is used ?

  • How many equity subaccounts are needed ? Is EQUITYACCT:COMMPAIR sufficient ? Can per-direction reports still be achieved by filtering on amount sign ?

  • Should inferred postings be displayed by print --infer-equity, or only by print --infer-equity --explicit ? (-x/--explicit is required to see other infered things like amounts and prices)

  • How much of this applies equally well to currency exchanges, investment purchases, and investment sales, in principle ? How much of that commonality should we expose for best UX ?

  • How does this relate to the idea of lot identity, and generating lot subaccounts ? Should lot subaccounts exist on the asset(/liability) side, on the equity side, or both ?

Editor configuration

If you edit your journals (and other hledger data files) with a text editor, you want that frequent task to be as pleasant and non-tedious as possible. So it's worth using a powerful text editor - one with comfortable copy/paste, search & replace, and perhaps more advanced features like macros.

For the popular text editors there are helper modes/extensions which can make editing hledger journal files much more convenient. These provide things like syntactic highlighting, auto indentation, and tab completion of account names. You can find a list of these extensions at
https://plaintextaccounting.org/#editor-support.
The ones with "hledger" in their name are designed specifically for working with hledger journals, while the ones with "ledger" in their name are not, but can often work well with hledger as well (eg: ledger-mode).

Here are more details and tips.

Emacs

ledger-mode

https://github.com/ledger/ledger-mode (manual), for Emacs, is the most used and maintained helper mode for hledger and Ledger files.

It has some hard-coded dependence on Ledger's command-line interface, so does not work perfectly with hledger, whose CLI is similar but not identical. There are a few ways to get around this:

  • Most common: configure ledger-mode to run hledger, and accept that some more advanced features (reports, reconcile-mode) will not work for now; help welcome. Configure ledger-mode this way:

    1. M-x customize-group, ledger-exec
    2. change ledger-binary-path to hledger
  • Or: keep your hledger journal 100% Ledger-compatible, and let ledger-mode run ledger as it usually does. Unless you are a Ledger user who wants to run both tools, you may find this too limiting.

  • Or: set up compatibility scripts emulating the ledger command set and CLI with hledger. For example: ledger-display-balance-at-point (C-c C-p) runs ledger cleared ACCT. hledger doesn't have a "cleared" command, but you could make one similar to Ledger's using an add-on script: hledger-cleared.sh in $PATH containing:

    #!/bin/sh
    hledger balance -N -C "$@"
    

    This approach can solve some of the incompatibilities, but it's a hassle.

More tips:

To toggle a transaction's cleared status: move point to it, C-c C-e.

To toggle just a posting's status: move point to it, C-c C-c.

#367 ledger-mode setup for hledger needs documenting has more tips to be collected here.

Here are ledger-mode's hledger-related issues.

hledger-mode

https://github.com/narendraj9/hledger-mode
An alternative to ledger-mode, written specifically for hledger. Has some different features. Less actively maintained.

flycheck-hledger

https://github.com/DamienCassou/flycheck-hledger (2021) provides realtime indication of problems in your journal. It can be used with ledger-mode or hledger-mode.

C-x ` steps to the next problem in the current file.
C-u C-x ` restarts the scan from the top.
A description should appear in the message area, but Emacs may hide it behind "...locus..." messages; you can fix that by customising the next-error-verbose variable to off.

Sample config:

(use-package flycheck-hledger
  :after (flycheck ledger-mode)  ; or hledger-mode
  :ensure t
  :demand t
  :custom
  (flycheck-hledger-strict t) 
  (flycheck-hledger-checks '("ordereddates" "recentassertions"))   ; extra checks from https://hledger.org/hledger.html#check: ordereddates, uniqueleafnames, payees, recentassertions, tags..
  ;(flycheck-hledger-executable "hledger")
  )

Currently flycheck-hledger always runs hledger with the --auto flag, so be aware that any auto posting rules will be active.

flymake-hledger

https://github.com/DamienCassou/flymake-hledger/ (2023) is a more recent alternative to flycheck-hledger, using the built-in Flymake rather than the third-party Flycheck feature.

Here are some early configuration notes, using use-package:

;; Enable verbose use-package debug info when starting with --debug-init
;; (when init-file-debug
;;   (setq use-package-verbose t
;;         use-package-expand-minimally nil
;;         use-package-compute-statistics t
;;         debug-on-error t))

;; Configure flycheck-like keybindings for flymake:
(use-package flymake
  :bind (
         :map flymake-mode-map
         ("C-c ! n" . flymake-goto-next-error)
         ("C-c ! p" . flymake-goto-prev-error)
         ("C-c ! l" . flymake-show-buffer-diagnostics)  ; a new list for each buffer, unlike flycheck
         ("C-c ! v" . flymake-switch-to-log-buffer)))

(use-package flymake-hledger
  :load-path "~/src/flymake-hledger"
  :after (ledger-mode flymake)

  :hook (
  (ledger-mode . flymake-hledger-enable)
  ;; Make C-x ` work ?
  ;; XXX Both of these work only in the first file opened; debugging needed.
  ;; (ledger-mode . (lambda () (setq next-error-function 'flymake-goto-next-error)))
  ;; (ledger-mode . (lambda () (setq next-error-function (lambda (num reset) (when reset (goto-char (point-min))) (flymake-goto-next-error num)))))
  )

  :custom
  (flymake-show-diagnostics-at-end-of-line t)  ; might require Emacs 30
  (flymake-suppress-zero-counters t)
  (flymake-hledger-checks '("accounts" "commodities" "balancednoautoconversion" "ordereddates")) ; "recentassertions" "payees" "tags" "uniqueleafnames" https://hledger.org/hledger.html#check
  )

To extra checks for a specific journal file, add a line like this near the top:

; -*- eval:(add-to-list 'flymake-hledger-checks "recentassertions" t); -*-

org babel

org babel (ob) is the system for evaluating code blocks embedded in org outlines. hledger reports can be embedded within an org outline in this way, and easily evaluated inline or exported in various formats. This is a nice way to save and organise and interactively update reports.

In 2021 this functionality was moved to an optional package, org-contrib. To enable it:

  • M-x list-packages, install org-contrib

  • In your emacs config, add: (require 'ob-hledger)
    (and evaluate it with C-M-x, or a restart)

  • In any org file, add hledger commands like this:

    #+begin_src hledger :cmdline -f ~/finance/2022.journal balance
    #+end_src
    
  • To evaluate the command inline, press C-c C-c with point (cursor) inside the above line

  • To update all such reports in the file, press C-c C-v b

  • To export all reports:

    • as html, and open in browser: C-c C-e h o
    • as html: C-c C-e h h
    • as UTF-8 text: C-c C-e t U
    • as markdown (if configured): C-c C-e m o
    • etc.
  • To export only the reports in the current subtree:

    • configure it at top of org file: # -*- org-export-initial-scope:subtree; -*-)
    • put point in the desired subtree before exporting as above

See also Using Ledger for Accounting in Org-mode with Babel

calc

Calc can help perform arithmetic on amounts in the buffer during data entry. Eg to split an amount by two:

  • put point at the start of the amount, after the currency symbol
  • C-x * w 2 / q

Misc

A helper to browse TODO tags in the journal:

(defun journal-todos nil (interactive) (lgrep "TODO:" "current.journal" "~/finance" nil))

Vim

vim-ledger

https://github.com/ledger/vim-ledger

hledger-vim

https://github.com/anekos/hledger-vim

timedot-vim

https://github.com/linuxcaffe/timedot-vim

Neovim

Run :TSInstall ledger to enable nvim-treesitter for hledger.

VS Code

hledger-vscode

https://github.com/mhansen/hledger-vscode

Miscellaneous

From the mail list, a trick for aligning transaction amounts: "Space-indent the account, tab-indent the amount, set a large tab stop."

Exporting from hledger

Many finance apps have a way to import CSV files from financial institutions. You can produce similar CSV with hledger's register command. Export one account and one currency at a time. This helps keep the CSV simple and importable. Eg:

$ hledger -f examples/sample.journal reg -O csv checking cur:'\$'
"txnidx","date","code","description","account","amount","total"
"1","2008-01-01","","income","assets:bank:checking","$1","$1"
"2","2008-06-01","","gift","assets:bank:checking","$1","$2"
"3","2008-06-02","","save","assets:bank:checking","$-1","$1"
"5","2008-12-31","","pay off","assets:bank:checking","$-1","0"

The new aregister command (currently in master) is best for this, since it guarantees one record per transaction even with complex multi-posting transactions, and provides the (abbreviated) other account names, making categorisation easier when importing:

$ hledger -f examples/sample.journal areg checking -O csv cur:'\$'
"txnidx","date","code","description","otheraccounts","change","balance"
"1","2008-01-01","","income","in:salary","$1","$1"
"2","2008-06-01","","gift","in:gifts","$1","$2"
"3","2008-06-02","","save","as:ba:saving","$-1","$1"
"5","2008-12-31","","pay off","li:debts","$-1","0"

hledger supports other output formats, including HTML, JSON and SQL. Not all formats are supported by all commands/reports though. For a given report, you can check the --help or just try an output format to see if it has been added.

$ hledger -f examples/sample.journal reg checking -O sql
hledger: Sorry, output format "sql" is unrecognised or not yet implemented for this report or report mode.
$ hledger -f examples/sample.journal print checking -O sql
create table if not exists postings(id serial,txnidx int,date1 date,date2 date,status text,code text,description text,comment text,account text,amount numeric,commodity text,credit numeric,debit numeric,posting_status text,posting_comment text);
insert into postings(txnidx,date1,date2,status,code,description,comment,account,amount,commodity,credit,debit,posting_status,posting_comment) values
('1','2008-01-01',NULL,NULL,NULL,'income',NULL,'assets:bank:checking','1','$',NULL,'1',NULL,NULL)
,('1','2008-01-01',NULL,NULL,NULL,'income',NULL,'income:salary','-1','$','1',NULL,NULL,NULL)
,('2','2008-06-01',NULL,NULL,NULL,'gift',NULL,'assets:bank:checking','1','$',NULL,'1',NULL,NULL)
,('2','2008-06-01',NULL,NULL,NULL,'gift',NULL,'income:gifts','-1','$','1',NULL,NULL,NULL)
,('3','2008-06-02',NULL,NULL,NULL,'save',NULL,'assets:bank:saving','1','$',NULL,'1',NULL,NULL)
,('3','2008-06-02',NULL,NULL,NULL,'save',NULL,'assets:bank:checking','-1','$','1',NULL,NULL,NULL)
,('5','2008-12-31',NULL,'*',NULL,'pay off',NULL,'liabilities:debts','1','$',NULL,'1',NULL,NULL)
,('5','2008-12-31',NULL,'*',NULL,'pay off',NULL,'assets:bank:checking','-1','$','1',NULL,NULL,NULL)
;

hledger2psql is a tool that exports a hledger journal to a postgres database.

Related discussion: https://groups.google.com/g/hledger/c/HS1Wd2iUSgA/m/oqVhSEf4AgAJ

Frequently Asked Questions

Welcome! This FAQ is for all hledger-related topics, for now. If you have additions or improvements, please click the "edit this page" link at the bottom, or chat with us.

Accounting

What's accounting ?

Accounting means keeping track of the flow and whereabouts of things you value, such as money or time, and using this information for insight, planning and decision-making. Here's 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.

The hledger project

What's hledger ?

One of the best tools for doing Plain Text Accounting. It's free and you can read all about it at the https://hledger.org home page.

Why was hledger created ?

I needed to make working on finances 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.

hledger and Plain Text Accounting

We use another system, we don't need this ?

Every tool has strengths and weaknesses. hledger is lightweight, flexible and relatively easy to glue into other systems; it might be worth exploring as a complementary tool.

How do you collaborate with accountants and the non-PTA world ?

Depending on their needs, you send them a few standard reports (balance sheet, income statement, itemized account registers or a full transaction journal)

  • as plain text (optionally spruced up with your own templates)
  • or as HTML
  • or as PDF
  • or as CSV they can import into Excel and elsewhere

Must I enter data in a text editor ??

No. A good text editor can be a very efficient way to work on your data, but there are other ways:

  • use a terminal-based data entry tool like hledger add or hledger-iadd
  • use a web-based data entry tool like hledger-web
  • use a phone-based data entry app like MoLe
  • import CSV data, avoiding manual data entry.

What is a good set of account names to start with ?

The chart of accounts (list or tree of account names) will vary according to your language and your accounting situation, and will usually evolve as your needs change. Currently hledger does not have any built-in default chart of accounts. You can start small: name just the accounts you need, as you need them. Or, copy a chart of accounts that seems suitable, from other accounting software or from the web. Some examples can be found at https://plaintextaccounting.org/Choosing-accounts.

What can hledger do for me ?

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.

hledger and other software

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

Using hledger

How do I set environment variables like LEDGER_FILE (persistently) ?

See hledger manual > Setting LEDGER_FILE.

How should I organise files ?

See https://plaintextaccounting.org/Getting-started-FAQ#how-to-organise-files

Why does this entry give a "no amount" error even though I wrote an amount ?

2019-01-01
  a $1
  b

Because there's only a single space between a and 1, so this is parsed as an account named "a 1", with no amount. There must be at least two spaces between account name and amount.

Why does this journal fail strict account checking even though I declared all accounts ?

account assets:bank:checking ; my bank account
account equity               ; equity

2023-01-01
    equity
    assets:bank:checking   $1000

Because there's only a single space between assets:bank:checking and the ; comment, so the comment is parsed as part of the account name. (hledger accounts shows this.) There must be at least two spaces between an account name and anything that follows it.

Why do some directives not affect other files ? Why can't I include account aliases ?

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.

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 ?

Use hledger 1.29+, and just specify the desired start date, eg hledger is -Y -b 2020/4/15 or hledger is -p 'yearly from 2020/4/15'. With older hledger versions, you can approximate it with -p 'every 12 months from 2020/4 or -p 'every 365 days from 2020/4/15'.

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.

Workarounds:

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

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

When importing CSV from two bank accounts, how do I avoid duplicate entries for a transfer between them ?

You can have one of the rules files skip that transaction. Eg if you have checking and savings CSVs, in savings.csv.rules you might have a rule like

# already imported from checking
if TRANSFER FROM .*CHECKING
 skip

Other options: you could remove one of the entries manually. Or you could import both entries, but have them transfer to and from an imaginary third account (but this creates extra noise in your journal and reports).

Customising hledger

How do I install hledger CSV rules for my financial institutions ?

git clone the main hledger repo, and look in examples/csv/ for a rules file you can copy to your financial working directory. If your financial institution is not there yet, please use these for inspiration, ask the #hledger chat for help, and send a pull request contributing your working rules to the repo.

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.

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.

Features

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. There is also a growing ecosystem of complementary tools and apps, and if you are a programmer you can easily build your own integrations.

How is it different from other accounting software ?

hledger is a Plain Text Accounting system, which means:

  • Data is stored in simple plain text files, which can be easily read by humans, tracked with version control software such as Git, and maintained with text processing tools. This facilitates auditing, portability, and longevity of your valuable accounting data.

  • The data format is flexible and easy to write or generate, but hledger can check it and prevent many kinds of error. This, plus the transparency and version control, provides confidence in your data and reports.

  • Data and software is kept on your local computer, keeping your financial data private and under your control. But if you should want to collaborate, version control makes that easy.

  • You can edit data with your favourite text editor or IDE, or a data entry UI, or convert and import data from other formats (eg CSV from your banks).

  • There is a fast command-line interface, which makes the tool easy to script, automate, and integrate into custom workflows.

  • The model of operation is simple: put a log of transactions in, get reports out.

  • It is fast, lightweight, non-distracting, and great for learning more of double-entry bookkeeping and accounting.

What can it do, in detail ?

hledger can:

  • list your transactions, payees, currencies/commodities, accounts, statistics
  • show the hierarchy of accounts and subaccounts
  • show the transactions affecting any account, and calculate its running balance
  • make a balance sheet, showing your asset and liability account balances
  • make a cashflow report, showing changes in your cash assets
  • make an income statement, showing your revenues and expenses
  • show simple bar charts by period
  • show purchase costs/selling prices
  • show market values in any currency at any valuation date
  • calculate the rate of return of a savings account or investment
  • make reports from timeclock or timedot time logs
  • make reports from any CSV/SSV/TSV file
  • make reports from any data which you have converted to a hledger-readable format

It can slice, dice, and present your data in different ways:

  • filter out just the items or time period you're interested in
  • show multiple periods side by side
  • summarise accounts to a certain depth to give the big picture
  • rewrite or pivot account names to transform reports
  • output reports as plain text, HTML, CSV, TSV, JSON, SQL...
  • run as a live-updating terminal UI, for fast interactive exploration
  • run as a web app, allowing remote/multi-user browsing and data entry
  • run as a JSON web API, for integrating with custom apps

If you add a few directives to the file, hledger can:

  • combine multiple files / data sets
  • generate recurring transactions by rule
  • generate extra postings (splits) on transactions by rule
  • show a forecast of future activity, eg to help with planning
  • make a budget report, showing your budget goals and performance by account and period

Also, it can:

  • generate interest transactions by rule
  • help you enter new transactions with prompts or a terminal UI
  • help you convert and import new transactions from external sources, eg banks
  • be used as a library in a quick Haskell script or compiled program

Batteries are included

hledger comes with multiple user interfaces that just work:

A command-line tool (CLI). Transactions are stored in a journal file which you can edit with a text editor. From this hledger produces various reports, without changing your data.

A live-updating terminal interface (TUI), that lets you review account balances and transactions quickly. (screencast)

A zero-setup web interface (WUI), allowing terminal-free, point-and-click usage. Run it privately on your local machine, or on a server to collaborate with others. (demo).

A haskell library. You can write scripts, add-on commands, or financial applications as powerful as hledger itself.

It's relatively easy

Within its scope - a Plain Text Accounting tool, which is by nature slightly technical - hledger aims to be intuitive, learnable and highly usable, learning from other PTA tools and taking PTA to a higher level of usability and utility. Here are some things it provides out of the box:

Dependable

hledger strives to be comfortable to use, to be absolutely dependable, to provide real-world value, and to never waste your time. It provides:

  • Robust installation: multiple options are provided for binary and source installation. Building from source is reliable and consistent across platforms.

  • Robust execution: hledger is written in Haskell, a modern, highly-regarded programming language. Runtime failures are minimised by Haskell's memory management and strong compile-time type checking. Failures caused by user input are reported clearly and promptly.

  • Robust features: built-in commands and options combine well with one another, and are expected to do something sensible in all cases, with all kinds of input.

  • Robust parsing: dated items, such as balance assertions and balance assignments, are processed in date order. Assertions/assignments with the same date are processed in parse order. Multiple assertions/assignments within a single transaction work as you would expect.

  • Robust calculation: results are expected to always perfectly match what you would calculate on paper, up to 255 decimal places.

  • Robust reporting: reports are deterministic and not affected by the order of input files or data items except where that is part of their spec.

  • Robust testing: The software is continually tested by more than a thousand automated tests.

  • Robust documentation: all functionality is documented precisely, with a mnemonic permalink. User manuals for your hledger version are available online, and also built in for offline viewing. General and command-specific command line help is provided. We favour documentation-driven development.

Fast

Compatible

hledger is a rewrite of the pioneering Ledger CLI, aiming to build out the same core features to a higher level of quality, and to add new ones making it useful to more people. Ledger users will find the file formats and commands familiar, and with a little care can run both tools on the same data files. (You can read more about the origins and differences.)

hledger can read Beancount files, or vice versa, by converting them with the beancount2ledger and ledger2beancount tools.

Many tools exist for importing from other applications. Data can be exported as CSV, JSON or basic SQL.

Free Software

hledger is Free Software, with no purchase price or monthly fees. It is licensed under GNU GPLv3+, providing the strongest guarantee that you will always have the right to run, inspect, modify, or share it. It is actively maintained, with regular releases and a large chat room and other support resources.

github

But not yet...

  • ... easy for completely non-technical people to install and use (without a little help)
  • ... easy to use on a phone
  • ... equipped with mature, polished GUIs
  • ... or easy charts
  • ... able to download directly from banks and financial institutions
  • ... with as many investing-savvy users as Beancount
  • ... simple, clear and obvious for all needs, or masterable in a day (or a week).

Double Entry Bookkeeping, Plain Text Accounting, and Accounting in general are deep and rich topics; as a newcomer you might feel there's too both much and not enough to read, too much flexibility and not enough structure or guidance. You may need to pace yourself, ask the chat for tips, and learn through practice.

Forecasting

Some ways:

  • Enter future-dated transactions in your journal, commented out (with ; or comment)

  • Enter future transactions uncommented; use a query to exclude them from reports when needed (-e tomorrow or date:-tomorrow. hledger-ui hides them by default.)

  • Enter future transactions in a separate forecast.journal, which you can include when needed (eg, add -f forecast.journal).

  • Enter periodic transaction rules describing future transactions (recurring or non-recurring), and activate the forecast with --auto.

  • Budgeting and forecasting (2018) > Forecasting - reusing a budget's periodic transactions to generate a forecast.

Foreign trip expenses

From https://www.reddit.com/r/plaintextaccounting/comments/9r9cfj/beancount_price_and_cost :

  1. Before going to vacation to Europe, I borrowed 350 EUR, cash.
  2. I also took out of ATM 200 EUR, cash - now I know the price.
  3. I spent 500 EUR in trip, and I have 50 left.
  4. Now, after the trip, I exchanged some of my home currency to 300 EUR to give it back - and it's the different price from step two. So how do I write all this down?

My attempt follows. Notes:

  • When transactions occur on such trips, I sometimes know the USD amount spent, and sometimes the EUR amount. I sometimes know the total converted amount, and sometimes the conversion rate. I record whichever of these is more convenient.
  • After the trip, when reviewing expenses, I'll add a P market price directive covering the period of the trip, and use -V to see all expenses in home currency (USD).
; a hledger example based on colindean's
; hledger doesn't currently support the {} syntax, just @ or @@

2018-10-25 * vacation loan
    Liabilities:Loans:Vacation
    Assets:Cash                             350 EUR

2018-10-26 * ATM withdrawal
    Assets:Cash                             200 EUR @@ 220 USD  ; conversion price written out for clarity; redundant due to -225 USD below
    Expenses:Fees:CurrencyConversion          5 USD
    Assets:Bank                            -225 USD

2018-10-27 * food
    Assets:Cash                            -190 EUR
    Expenses:Vacation:Food

2018-10-27 * hotel
    Assets:Cash                            -310 EUR = 50 EUR    ; assert that Cash's EUR balance is now 50
    Expenses:Vacation:Hotel

2018-10-28 * withdraw more euros to repay loan
    Assets:Cash                             300 EUR @@ 360 USD  ; conversion rate has gone up to 1.20
    Expenses:Fees:CurrencyConversion          5 USD
    Assets:Bank                            -365 USD

2018-10-28 * repay vacation loan
    Liabilities:Loans:Vacation              350 EUR = 0 EUR     ; assert that euro loan is repaid
    Assets:Cash

; Conversion rate to use in reports for the trip period.
; You could declare each time it changed, eg:
; P 2018-10-25 EUR 1.10 USD
; P 2018-10-28 EUR 1.20 USD
; but hledger currently picks just one,
; and for expense reporting a rough average price is usually fine:
P 2018-10-25  EUR  1.15 USD

Here are a few different reports, for comparison:

Simple balance change report for all accounts. --flat and -Y help ensure a readable tabular layout here.

$ hledger bal --flat -Y
Balance changes in 2018:

                                  ||                 2018 
==================================++======================
 Assets:Bank                      ||          -590.00 USD 
 Expenses:Fees:CurrencyConversion ||            10.00 USD 
 Expenses:Vacation:Food           ||              190 EUR 
 Expenses:Vacation:Hotel          ||              310 EUR 
----------------------------------++----------------------
                                  || 500 EUR, -580.00 USD 

Adding the -B/--cost flag converts transaction amounts to the other commodity in the transaction, using the conversion rate specified in the transaction if any. This typically helps collapse the grand total to one commodity, so we can see it is zero here (expected, since we're showing all accounts).

$ hledger bal --flat -Y -B
Balance changes in 2018:

                                  ||                 2018 
==================================++======================
 Assets:Bank                      ||          -590.00 USD 
 Assets:Cash                      || -500 EUR, 580.00 USD 
 Expenses:Fees:CurrencyConversion ||            10.00 USD 
 Expenses:Vacation:Food           ||              190 EUR 
 Expenses:Vacation:Hotel          ||              310 EUR 
----------------------------------++----------------------
                                  ||                    0 

Adding the -V/--value flag instead converts report amounts using the market price effective on the reporting date (hledger prices and date can help identify that). The grand total of -5 USD here corresponds to our capital loss due to change in exchange rate (the price of a euro went from $1.10 to $1.20 while we still owed some):

$ hledger prices 
P 2018-10-25 EUR 1.15 USD
$ date
Fri Oct 26 15:03:00 PDT 2018
$ hledger bal --flat -Y -V
Balance changes in 2018:

                                  ||        2018 
==================================++=============
 Assets:Bank                      || -590.00 USD 
 Expenses:Fees:CurrencyConversion ||   10.00 USD 
 Expenses:Vacation:Food           ||  218.50 USD 
 Expenses:Vacation:Hotel          ||  356.50 USD 
----------------------------------++-------------
                                  ||   -5.00 USD 

The "exp" account query is added to show just the expenses. Now we can see their total.

$ hledger bal --flat -Y -V exp
Balance changes in 2018:

                                  ||       2018 
==================================++============
 Expenses:Fees:CurrencyConversion ||  10.00 USD 
 Expenses:Vacation:Food           || 218.50 USD 
 Expenses:Vacation:Hotel          || 356.50 USD 
----------------------------------++------------
                                  || 585.00 USD 

Or you might use the is/incomestatement command which is specialised for income/expense reporting. It's tabular and flat by default.

$ hledger is -V
Income Statement 2018/10/25-2018/10/28

                                  || 2018/10/25-2018/10/28 
==================================++=======================
 Revenues                         ||                       
----------------------------------++-----------------------
----------------------------------++-----------------------
                                  ||                       
==================================++=======================
 Expenses                         ||                       
----------------------------------++-----------------------
 Expenses:Fees:CurrencyConversion ||             10.00 USD 
 Expenses:Vacation:Food           ||            218.50 USD 
 Expenses:Vacation:Hotel          ||            356.50 USD 
----------------------------------++-----------------------
                                  ||            585.00 USD 
==================================++=======================
 Net:                             ||           -585.00 USD 

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:

Tutorials

Detailed step by step tutorials, with screenshots:

Videos

Manuals

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.

How-tos

Practical advice and examples for real-world tasks:

See also:

Support

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

Advice

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.

Or:

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

Imperfection

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

Fast, friendly, robust
plain text accounting software

hledger is...

Here's more about the Features. Don't hesitate to join the Discussion/Support 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.)

Examples

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

Declarations

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
assets
  bank
    checking
    savings
  cash
liabilities
  credit card
equity
  opening/closing
income
  salary
  gifts
expenses
  rent
  food
  gifts

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
    cash

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:


2023/2/1
biz:research  .... ..
fos:hledger   .... .... ....

2023/2/2
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:


"Date","Notes","Amount"
"2023/2/22","DEPOSIT","50.00"
"2023/2/23","TRANSFER TO SAVINGS","-10.00"

# bank.csv.rules  # this rules file tells hledger how to read bank.csv
skip 1
fields date, description, amount
currency $
account1 assets:bank:checking

if WHOLE FOODS
 account2 expenses:food

if (TO|FROM) SAVINGS
 account2 assets:bank:savings
$ hledger -f bank.csv print
2023-02-22 DEPOSIT
    assets:bank:checking          $50.00
    income:unknown               $-50.00

2023-02-23 TRANSFER TO SAVINGS
    assets:bank:checking         $-10.00
    assets:bank:savings           $10.00

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.

How to record journal entries

There are many ways to create hledger journal entries. Here are some options:

Interactively

With a text UI

With a web UI

On a phone or tablet

In a text editor

From scratch

  • Write the entry in full, using journal format.
  • As above, but also use editor addons to assist (like Emacs ledger-mode's TAB account completion and M-q alignment).

From templates

  • In Emacs with yasnippet configured (eg), enter the name of a snippet and complete with TAB

From past entries

  • Find, copy and modify a past entry in the journal
  • Or in Emacs ledger-mode, enter a date and partial description and complete with C-c TAB

From future entries

  • Move the entry from a separate file where you prepared it ahead of time (eg future.journal)

At the command line

From scratch

  • Write the entry using journal format, eg
    $ cat >>$LEDGER_FILE
    2023-01-01
        expenses:food   $10
        assets:cash
    
    <CTRL-D>
    
    or
    printf >>$LEDGER_FILE '2023-01-01\n  expenses:food  $10\n  assets:cash\n\n'
    

From past entries

From periodic transaction / auto posting rules

From custom scripts

  • If you have custom scripts to help with tasks, make them print or append journal entries when appropriate. Eg have your invoicing script add an invoice transaction to the journal.

By converting data

From bank CSV (or other character-separated values)

From timeclock, timedot or other journal files

  • Just read them directly with -f
  • Or, hledger import them to main journal

From other apps or data formats

How to figure out journal entries

When you're facing an unfamiliar bookkeeping situation where the journal entries aren't clear, try this:

  1. Write down the real world events to be modelled, in detail

  2. Write down the things you want to know

  3. Start with a simpler version of the scenario if necessary. If you get stuck below, return here and simplify more.

  4. Discover which accounts and transactions are needed to model the situation in the most principled and correct way. Take one event at a time, and try to write the journal entry for it, using hledger to check that each transaction is balanced and the account balances make sense. If needed, return to step 3. Or consult books or the internet for ideas - but carefully, since their terminology and examples will often be different and confusing.

  5. Experiment and discover reporting commands that show the things you want to know. If it seems not possible, you may need to revisit steps 3 or 4.

  6. Now consider, are there simpler accounts/transactions/reports that might be more convenient, yet still good enough ?

  7. Choose one approach. Document it if necessary so you'll remember. Save or automate the reporting commands so you'll remember those.

  8. Put it into practice and refine over time to improve clarity and usability.

Importing CSV data

hledger has a powerful CSV converter built in. After saving a few declarations in a "CSV rules file", it can read transactions from almost any CSV file. This is described in detail in the hledger manual, but here are some quick examples.

Say you have downloaded this checking.csv file from a bank for the first time:

"Date","Note","Amount"
"2012/3/22","DEPOSIT","50.00"
"2012/3/23","TRANSFER TO SAVINGS","-10.00"

Create a rules file named checking.csv.rules in the same directory. This tells hledger how to read this CSV file. Eg:

# skip the headings line:
skip 1

# use the first three CSV fields for hledger's transaction date, description and amount:
fields date, description, amount

# specify the date field's format - not needed here since date is Y/M/D
# date-format %-d/%-m/%Y
# date-format %-m/%-d/%Y
# date-format %Y-%h-%d

# since the CSV amounts have no currency symbol, add one:
currency $

# set the base account that this CSV file corresponds to
account1 assets:bank:checking

# the other account will default to expenses:unknown or income:unknown;
# we can optionally refine it by matching patterns in the CSV record:
if (TO|FROM) SAVINGS
  account2 assets:bank:savings

if WHOLE FOODS
  account2 expenses:food

You can print the resulting transactions in any of hledger's output formats:

$ hledger -f checking.csv print
2012-03-22 DEPOSIT
    assets:bank:checking          $50.00
    income:unknown               $-50.00

2012-03-23 TRANSFER TO SAVINGS
    assets:bank:checking         $-10.00
    assets:bank:savings           $10.00

Or run reports directly from the CSV:

$ hledger -f checking.csv bal
              $40.00  assets:bank:checking
              $10.00  assets:bank:savings
             $-50.00  income:unknown
--------------------
                   0

Or import any new transactions, saving them into your main journal:

$ hledger import checking.csv --dry-run 
; would import 2 new transactions from checking.csv:

2012-03-22 DEPOSIT
    assets:bank:checking          $50.00
    income:unknown               $-50.00

2012-03-23 TRANSFER TO SAVINGS
    assets:bank:checking         $-10.00
    assets:bank:savings           $10.00

$ hledger import checking.csv
imported 2 new transactions from checking.csv

hledger import ignores transactions it has seen before, so it's safe to run it repeatedly. (It creates a hidden .latest.checking.csv file in the same directory. If you need to forget the state and start over, delete this.)

Customize the default "unknown" accounts

When converting CSV, hledger uses the account names income:unknown and expenses:unknown as defaults. Normally when you see these, you will want to add CSV rules to set a more specific account name. But you may want to change these defaults, eg into your language.

Method 1: You can add rules something like these, as the first account2 rules:

# set account2 to this:
account2 Revenues:Misc

# change it to Expenses:Misc if the csv "amount" field contains a minus sign:
if %amount -
 account2 Expenses:Misc

# override it with more specific rules below...

Method 2: You can use --alias options to rewrite those account names. With hledger 1.20+:

$ hledger -f checking.csv --alias income:unknown=Income:Misc --alias expenses:unknown=Expenses:Misc print
2012-03-22 DEPOSIT
    assets:bank:checking          $50.00
    Income:Misc                  $-50.00

2012-03-23 TRANSFER TO SAVINGS
    assets:bank:checking         $-10.00
    assets:bank:savings           $10.00

(Before hledger 1.20, --alias only worked with journal format so you had to pipe it like this:)

$ hledger -f checking.csv print | hledger -f- --alias income:unknown=Income:Misc --alias expenses:unknown=Expenses:Misc print

See also

Full documentation of CSV conversion, and more rules examples, can be found in the hledger manual.

A collection of CSV rules can be found in examples/csv/ in the main hledger repository. There is also a Makefile to help manage and import multiple CSV files. I keep journals, csv files and csv rules in one finance directory; the makefile moves downloaded csv files there and imports them. The procedure could be:

  • In browser: download one or more CSVs manually
  • In terminal: make -C ~/finance Import

There are many other CSV conversion tools (nine CSV->*ledger tools at last count), linked at plaintextaccounting.org -> data import/conversion.

Install

The current hledger release is 1.33. 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 Mac, Windows, GNU/Linux (RSS feed):
hledger release binaries release binaries

Packaged binaries

Homebrew (Mac, Linux, WSL)

Homebrew brew install hledger

Windows

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

Linux

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

Fedora_41 sudo dnf install hledger
Fedora_40
Fedora_39
Fedora_38
Fedora 37
more..

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
ubuntu_23_10
ubuntu_23_04
ubuntu_22_04
ubuntu_20_04
ubuntu_18_04
ubuntu_16_04
more..

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

BSD

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

Docker (Linux, Mac, Windows)

Docker docker pull dastapov/hledger
more..

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

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   # <- 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 with ghcup or from its website. Then:

stack update
stack install --verbosity error --resolver lts-22 \
    hledger-lib-1.33 hledger-1.33 hledger-ui-1.33 hledger-web-1.33

On MS Windows, run this instead:

stack update
stack install --verbosity error --resolver lts-22 \
    hledger-lib-1.33 hledger-1.33 hledger-ui-1.33 hledger-web-1.33 \
    process-1.6.19.0 Cabal-3.10.1.0 haskeline-0.8.2.1 vty-windows-0.2.0.2

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 a a supported version of GHC (8.10 - 9.8). Then:

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

Build in a source checkout

This is the best way to build if you'd like to customise or contribute to hledger. Use git to get the source code for 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

Then:

cd hledger

and you can build and install with:

stack update
stack install

or:

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-...
$ hledger-ui --version
hledger-ui 1.33-...
$ hledger web --version
hledger-web 1.33-...

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

PATH

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.

LANG

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
C
$ export LANG=C.UTF-8    # or en_US.UTF-8, fr_FR.utf8, etc.
$ echo $LANG
C.UTF-8

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.

Invoicing

Freelancers and businesses send invoices to clients to request payment.

See common journal entries and https://wiki.plaintextaccounting.org/Invoicing

Reports

With invoices and payments recorded as above, you can track unpaid invoices:

$ hledger bal receivable:supercompany

or list all invoices and payments:

$ hledger areg receivable:supercompany

or just invoices:

$ hledger areg receivable:supercompany amt:'>0'

or just payments:

$ hledger areg receivable:supercompany amt:'<0'

Creating Invoices

How to translate the data from your ledger into a professional-looking invoice you can send to clients ?

You can create the invoice manually or semi-manually, eg using a tool like Freshbooks, and copy-paste the numbers in.

Or you can automate this somehow. There are few ready-made tools for this, because needs are so diverse.

But you'll find some useful starter scripts in hledger's examples/invoicing directory.

See also

Mobile apps

Entering expenses on the spot using a mobile device can be convenient. One of the challenges is finding apps that focus on making this efficient. Here are some options. See also:

https://cone.tangential.info/wiki/Mobile-ledgers
https://plaintextaccounting.org/#ui-mobile

Apps that talk to hledger

MoLe (Android, GNU GPL v3+)

https://mole.ktnx.net
https://git.ktnx.net/?p=mobile-ledger.git
A data entry app that talks to a hledger-web (1.14+) server.

MoLe-1 MoLe-2

Apps with journal file export

Cashier (PWA, GNU GPL v3)

https://gitlab.com/alensiljak/cashier
demo
A progressive web application that can be used on desktop and (offline!) on web-capable mobile devices for entering transactions and viewing balances. The transactions can be exported as a (h)ledger file. The future plans include data synchronization with an instance of hledger-web.

Cashier options Cashier new transaction screen

cone (Android, GNU GPL v3)

https://github.com/bradyt/cone
Currently implements offline data entry, and saves a local *ledger file, which can be synced with a server via Syncthing.

Ledger Expense Tracking (Android)

https://github.com/jduepmeier/ledger-app
google play
Expense tracking app with *ledger export.

Apps with CSV export

The general workflow here is that every so often you manually initiate a CSV export from the app. Typically the app starts up a temporary HTTP server and you can fetch the data to your main machine with curl. Then, with suitable CSV rules, either run hledger reports directly from the CSV file, or convert it/import the new transactions into a more permanent journal file. A script or Makefile to automate this can be helpful.

GnuCash for Android

https://github.com/codinguser/gnucash-android
google play
Mobile UI for the mature GnuCash desktop accounting app.

GnuCash for Android accounts GnuCash for Android transactions GnuCash for Android reports

Eternity (IOS)

http://www.komorian.com/eternity.html
Excellent time tracking app. These CSV rules can be used to convert its CSV export to *ledger format.

XpenseTracker, BizXpenseTracker (IOS)

http://www.silverwaresoftware.com/XpenseTracker.html
Comprehensive and serviceable money & time tracking apps. CSV rules, Makefile

Apps with other ways to export

Money Manager Ex for Android (Android, GNU GPL v3)

http://android.moneymanagerex.org, https://github.com/moneymanagerex/android-money-manager-ex
Android port of the Money Manager Ex cross platform finance application. The MoneyManagerExLib python library can be used to convert its db to *ledger format.

MyExpenses (Android, GNU GPL v3+)

http://www.myexpenses.mobi, https://github.com/mtotschnig/MyExpenses
GPL personal finance manager for Android.

Export to *ledger format: https://github.com/ony/ledger-myexpenses (2017)

Sync to/from Ledger: https://gitlab.com/IanTwenty/ledgermyexpenses (2023)

Multicurrency tutorial (2018)

Anya begins using hledger without any currency symbols. She adds some journal entries like this (not bothering with descriptions, either):

2018/11/01
  income:gifts
  assets:bank          1000
    
2018/11/02
  assets:bank
  expenses:food         500

She knows hledger is filling in the missing amounts, which can be seen with print's -x/--explicit flag:

$ hledger print -x
2018/11/01
    income:gifts            -1000
    assets:bank              1000

2018/11/02
    assets:bank              -500
    expenses:food             500

The balance command with no arguments shows all balance changes. The total is zero, as Anya expects - each transaction sums to zero, and all transactions are included in this report, so the report also sums to zero:

$ hledger bal
                 500  assets:bank
                 500  expenses:food
               -1000  income:gifts
--------------------
                   0

Unlike partial balance reports (omitting some accounts), which typically do not have a zero total:

$ hledger bal food
                 500  expenses:food
--------------------
                 500

Anya maintains a popular free software project. She remembers that she added a Liberapay button to the project website yesterday, allowing donations. Her native currency is rubles, but Liberapay pays out US dollars or euros.

She realises she had better start tracking currencies in her journal or things will get confusing. So she adds currency symbols throughout her journal:

2018/11/01
  income:gifts
  assets:bank         ₽1000
    
2018/11/02
  assets:bank
  expenses:food        ₽500

Thinking ahead, she sees that entering euro symbols will be a bit unergonomic on her keyboard. She thinks perhaps she'll use standard alphabetic currency codes instead, and on the right-hand side:

2018/11/01
  income:gifts
  assets:bank          1000 RUB
    
2018/11/02
  assets:bank
  expenses:food         500 RUB

But she finds this a bit verbose. She decides to use single letters - R for rubles:

2018/11/01
  income:gifts
  assets:bank          1000 R
    
2018/11/02
  assets:bank
  expenses:food         500 R

Now her reports show the currency symbol:

$ hledger bal 
               500 R  assets:bank
               500 R  expenses:food
             -1000 R  income:gifts
--------------------
                   0

And she is ready for multicurrency accounting. Just in time, because next day a donation of 10 euros arrives! She records it, using E for euros:

2018/11/01
  income:gifts
  assets:bank          1000 R
    
2018/11/02
  assets:bank
  expenses:food         500 R

2018/11/03
  income:foss
  assets:liberapay       10 E

Now she has a multicurrency journal, and the balance report shows both currencies:

$ hledger bal 
                10 E        
               500 R  assets
               500 R    bank
                10 E    liberapay
               500 R  expenses:food
               -10 E        
             -1000 R  income
               -10 E    foss
             -1000 R    gifts
--------------------
                   0

However, it's a bit confusing. The assets and income parent accounts now have multicurrency balances, and each currency is displayed on its own line. She tries flat mode, and finds it clearer:

$ hledger bal --flat
               500 R  assets:bank
                10 E  assets:liberapay
               500 R  expenses:food
               -10 E  income:foss
             -1000 R  income:gifts
--------------------
                   0

But she has heard that hledger's tabular output is best for multicurrency reports, always showing amounts on one line. She starts using that, adding one of the report interval flags (-Y/--yearly) to activate it:

$ hledger bal -Y
Balance changes in 2018:

                  ||    2018 
==================++=========
 assets:bank      ||   500 R 
 assets:liberapay ||    10 E 
 expenses:food    ||   500 R 
 income:foss      ||   -10 E 
 income:gifts     || -1000 R 
------------------++---------
                  ||       0 

Anya requests a withdrawal of the Liberapay funds to her bank. Her bank holds rubles, so the euros will get converted. She's not sure of the exact exchange rate or fees, but next day, when the transaction clears, she can see that 10 euros left her liberapay account and 750 rubles arrived in her bank account. She decides to just record that:

2018/11/01
  income:gifts
  assets:bank          1000 R
    
2018/11/02
  assets:bank
  expenses:food         500 R

2018/11/03
  income:foss
  assets:liberapay       10 E

2018/11/04
  assets:liberapay      -10 E
  assets:bank           750 R

This is her first multicurrency transaction. She hasn't written the exchange rate explicitly, but the manual says hledger can figure it out. It seems to work:

$ hledger bal  -Y
Balance changes in 2018:

               ||         2018 
===============++==============
 assets:bank   ||       1250 R 
 expenses:food ||        500 R 
 income:foss   ||        -10 E 
 income:gifts  ||      -1000 R 
---------------++--------------
               || -10 E, 750 R 

However, two things surprise her. First, where has the liberapay account gone ? She remembers that balance reports hide zero-balance accounts by default, and adds -E/--empty to show it. (She also notes that zero amounts are displayed without a currency symbol, and would be a little clearer with currency symbols on the left):

$ hledger bal  -YE
Balance changes in 2018:

                  ||         2018 
==================++==============
 assets:bank      ||       1250 R 
 assets:liberapay ||            0 
 expenses:food    ||        500 R 
 income:foss      ||        -10 E 
 income:gifts     ||      -1000 R 
------------------++--------------
                  || -10 E, 750 R 

Second, the balance report is now showing a non-zero total. The individual euro and ruble totals look correct, but why isn't it zero ? Is the journal unbalanced ?

Anya asks for help on the #hledger IRC channel and is advised to add the -B/--cost flag. Sure enough, the total is now zero:

$ hledger bal -YEB
Balance changes in 2018:

                  ||         2018 
==================++==============
 assets:bank      ||       1250 R 
 assets:liberapay || 10 E, -750 R 
 expenses:food    ||        500 R 
 income:foss      ||        -10 E 
 income:gifts     ||      -1000 R 
------------------++--------------
                  ||            0 

But now the liberapay account, which should be empty, is showing a positive euro and negative ruble balance. As if one had not been converted into the other. Why is this ?

With a little help, Anya goes troubleshooting. Inspecting the multicurrency transaction with print -x (and a date filter to exclude the rest) shows how hledger has parsed it:

$ hledger print -x date:20181104
2018/11/04
    assets:liberapay    -10 E @@ 750 R
    assets:bank                  750 R

The manual makes this a bit clearer. Anya wrote the entry in costs style 3 ("let hledger infer the price that balances the transaction"). hledger has converted this to style 2 ("@@ TOTALPRICE after the amount"), recording that the 10 euro were priced at 750 rubles in this transaction.

With -B added, the 10 euro is converted to its cost in rubles:

$ hledger print -x date:20181104 -B
2018/11/04
    assets:liberapay          -750 R
    assets:bank                750 R

The register command shows how the balance reports above calculate the liberapay balance. Without -B: 10 euro are added, 10 euro are removed, the liberapay account's end balance is zero:

$ hledger reg liberapay
2018/11/03                     assets:liberapay               10 E          10 E
2018/11/04                     assets:liberapay              -10 E             0

With -B: 10 euro are added, 750 rubles are removed, the liberapay account's end balance is "10 euro, -750 rubles". (With each currency on its own line, again. Also, it seems that register aligns the account name with the top amount, unlike the balance command):

$ hledger reg liberapay -B
2018/11/03                      assets:liberapay              10 E          10 E
2018/11/04                      assets:liberapay            -750 R          10 E
                                                                          -750 R

In summary, it seems that the balance report must sum either the primary posting amounts (bal), or the cost amounts (bal -B), consistently for both the account balances above the line, and the total below the line. Otherwise the total would be incorrect. Which means that one or the other of these will be displayed as an unconverted multicurrency amount.

Anya decides to find out more about the other currency-related flag: -V.

TBD:

  • declaring a market price corresponding to the price in the fourth transaction ( P 2018/11/01 E 75 R ) and adding -V will show everything completely in rubles (with or without -B, at least in this case), preserving the zero total

  • declaring an accurate market price instead ( P 2018/11/01 E 74.91 R ), there will be a small non zero total, which corresponds to the gain/loss due to exchanging at a slightly different price. After adding an explicit gain/loss transaction, the zero total is restored.

  • The new -X and --value options.

News

Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning or any time, tagged with TWIH:.

See also: Release notes

This Week In Hledger 2024-04-19

https://hledger.org/news.html#this-week-in-hledger-2024-04-19

sm Worked on release process, github workflows, platform build issues, mac arm release binaries, CLI commands list, a hledger add ANSI bug, misc doc updates, and released hledger 1.33.

Misc

You can see recent discussions at: https://matrix.hledger.org https://list.hledger.org https://forum.plaintextaccounting.org/latest https://www.reddit.com/r/plaintextaccounting/new https://fosstodon.org/search #hledger

Quotes

Been using ledger and hledger for about four years, love them dearly. I find ledger + Emacs ledger-mode the most convenient for daily use. Hledger has a somewhat nicer way of formatting reports on the CLI, making it useful for creating ad-hoc reports and digging into your data. --compns8-ng

I've been using hledger ... [it] has more features like balance sheet, income statement generation with a plain text file for the last 3 years and it's been working out great. --rwbt


This Week In Hledger 2024-04-12

https://hledger.org/news.html#this-week-in-hledger-2024-04-12

sm Investigated/refactored/discussed a problem with boolean query expressions; disallowed date: in OR expressions #2178. Required process >=1.6.19.0 for HSEC-2024-0003. Updated docs: balance assertions, balancesheetequity.

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

Misc

You can see recent discussions at: https://matrix.hledger.org, https://list.hledger.org, https://forum.plaintextaccounting.org/latest, https://www.reddit.com/r/plaintextaccounting/new, https://fosstodon.org/search -> #hledger. Feel free to share interesting links (they don't have to be new) at the PTA forum.

Quotes

There are few things better than doing my taxes every year and being able to confirm every number on every form with simple #hledger queries (which lets me double check both my records and the forms themselves). --Michael Rees

I combine hledger csv output with Jupyter to generate the specific reports and charts I need. I.e business specific versus combined for taxes. --abhiyerra


This Week In Hledger 2024-04-05

https://hledger.org/news.html#this-week-in-hledger-2024-04-05

sm Doc updates (#2189, #2191, Cookbook#investing, A tax reporting setup). Added Brian Victor's hledger-txnsbycat.hs to the Scripts page.

On plaintextaccounting.org I started a project finance/donations page and public ledger; moved blog links off the front page in favour of the PTA forum; added a new video in Tamil language; updated Investing and trading, FAQ.

Misc

Dmitry Astapov fixed the roi command's handling of --value (#2190).

Ellane W raised thoughtful questions about when to use PTA vs simpler tools like paper.

lil5 announced a Go language client API for hledger-web.

You can see the recent discussion at eg https://matrix.hledger.org, https://list.hledger.org, https://forum.plaintextaccounting.org/latest, https://www.reddit.com/r/plaintextaccounting/new, https://fosstodon.org/search -> #hledger. Feel free to share interesting links (they don't have to be new) at the PTA forum.

Quotes

Having hledger have all my accounts, personal and business and being able to filter out appropriate reports has been great for seeing things like is the business a money pit or am I actually growing wealth over time. --abhiyerra

I use hledger and I love it. It has a learning curve (especially if you're new to double entry accounting) but it's rewarding.


This Week In Hledger 2024-03-29

https://hledger.org/news.html#this-week-in-hledger-2024-03-29

sm Doc rewrites, error message improvements.

Misc

  • More discussion of hledger-interest, a call for improvements, and a pull request by Dmitry Astapov.
  • Some discussion of exporting to Excel/spreadsheets.
  • How much attention to bookkeeping is just right ? blog post

Feel free to share your experience reports or interesting links (they don't have to be new) on the PTA forum.

Quotes

  • The initial setup of the books is pretty time consuming especially if you have a lot of accounts. I recommend starting your most important accounts and adding as you go along. --abhiyerra

This Week In Hledger 2024-03-22

https://hledger.org/news.html#this-week-in-hledger-2024-03-22

sm

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

Misc

Ilya Konovalov made multi-section balance reports always show a section total even when empty, fixing their CSV output. [#2186]

You can see recent discussions at:

https://matrix.hledger.org https://list.hledger.org https://forum.plaintextaccounting.org/latest https://www.reddit.com/r/plaintextaccounting/new https://fosstodon.org/search #hledger

Feel free to share interesting blog posts (they don't have to be new) at the PTA forum.

Quotes

A key advantage of hledger, and why I migrated from ledger, is that hledger internally orders transactions chronologically, whereas ledger inexplicably orders them in the order they appear in the file, irrespective of the date attached to them.--chromatin

hledger can validate date order, that seems to eliminate copy-paste-fail-to-update-date for me. --yencabulator

The reason I have hledger installed is for check ordereddates. --lvass


This Week In Hledger 2024-03-15

https://hledger.org/news.html#this-week-in-hledger-2024-03-15

sm No hledger dev work this week. The new PTA forum continues to work well.

Misc docker-finance, a new cryptocurrency-supporting workflow system using hledger-flow, and the older workflows Full-fledged hledger, hledger-flow and rtrledger, have been added at https://plaintextaccounting.org/#workflows.

To save time, I won't summarise discussions this week; you can see them at:

https://matrix.hledger.org https://list.hledger.org https://forum.plaintextaccounting.org/latest https://www.reddit.com/r/plaintextaccounting/new https://fosstodon.org/search #hledger

Feel free to share interesting blog posts (they don't have to be new) at the PTA forum.

Quotes

  • Way less painful than QuickBooks if you're also a programmer or adjacent. --dmoy

  • My favorite part of the (extensive) Haskell API is Hledger.Cli.Script. It re-exports all the most commonly used functions and data types, meaning you're just one import away from everything you need to get started.--wbadart


This Week In Hledger 2024-03-08

https://hledger.org/news.html#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 ?

Reddit

  • [hledger] Auto posting that excludes previously matched postings
  • Question: Hledger csv first account names in csv files?
  • Discourse for Plain Text Accounting discussions ?

Mastodon

  • online content platforms lock-in
  • PTA forum

Quotes

  • Journal's greenyellow really makes it pop and I think it will make us rich! --sm

  • I started setting up hledger this year and I’m with you for a small business it is absolutely great. --abhiyerra

  • In summary for me: hledger has better csv importing system and ledger has support for lots. I use both. --faustlast


This Week In Hledger 2024-03-01

https://hledger.org/news.html#this-week-in-hledger-2024-03-01

sm A lot of work this week: bugfixing, updating for GHC 9.8, and improving docs. We weren't ready to release 1.33 on March 1st, but it's near.

  • web: show zero amounts with their commodity symbol [#2140]
  • fix expr:'s OR-ing of open-ended dates [#2177]
  • made stats output more private by default, added memory stats
  • in unbalanced transaction error messages, show small imbalances more precisely
  • fixed all ghc 9.8 partial head/tail warnings
  • resolved remaining ghc 9.8-related issues in our dependencies
  • updated all stack configs, switched to nightly/ghc 9.8 for dev builds
  • added support for other unicode space characters in numbers
  • added --round option to close, like print's
  • fixed potential display of ambiguous balance assertion/assignment amounts [#2176]
  • refactored the budget report and fixed wrong omitting of parent accounts [#2071]
  • made budget report's CSV/TSV output show zero instead of empty cells
  • worked on a bugs dashboard
  • doc: fix description of commodity directive's scope [#2135]
  • doc: explain hledger's transaction balancing
  • doc: discuss debits, credits, and sign
  • doc: hledger manual cleanups, fixes, flow improvement
  • doc: FAQ: update why was hledger created
  • doc: hledger and ledger: update performance notes
  • plaintextaccounting.org: link https://lemmy.world/c/plaintextaccounting, a reddit alternative

Robert Nielsen On the hledger fan website, I take a look at needs, strategies, and spending. Also taxes and revisiting lowercasing. https://hledgerfan.com/needs-strategies-and-spending

Misc

Matrix chat wikipedia page, CSV rules, "Personal accounting in Ledger" book, unicode spaces in numbers, rvgp, loan bookkeeping, Open Collective, hledger manual size, file naming, emacs, visual studio code, aggregate shares bookkeeping..

Mail list

  • Forex Accounts Receivable, timing difference between invoice and payment
  • Unexpected behavior using 'expr:'

Reddit

  • 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

Quotes

Quite happy with my switch from GnuCash to hledger: already automated importing the (broken) CSV my bank provides, and getting an overview of average monthly expenses is a one-liner. --rigrig

The other nice part of hledger is the web UI (hledger-web), which is handy for checking out your transactions. --peterhajas


This Week In Hledger 2024-02-23

sm Miscellaneous updates:

  • use a symbolic link's target's directory for relative include paths
  • interpret glob patterns in $LEDGER_FILE
  • implicitly declare the date/date2/type tags
  • add close --assertion-type option
  • tweak close --assert's default description
  • stack:9.8: can now build all packages
  • doc: add Text encoding section
  • doc: clarify description/payee/note
  • doc: improve the tags doc
  • doc: clarify combining csv matchers
  • plaintextaccounting.org: Choosing accounts tips

Robert Nielsen Inspired by a recent podcast, I look at the theme of getting the most good from limited resources. Oh, and I don't forget to mention hledger. 🙂 https://hledgerfan.com/creating-a-positive-impact

Misc Jonathan Dowland added a dark theme to hledger-ui.

Mail list

  • Forex Accounts Receivable, timing difference between invoice and payment
  • Balancing "equity:conversion" split across postings.

Reddit

  • How do you all handle grouping your transactions? (business)
  • hledger: csv rule to match amount higher or equal to 200
  • hledger periodic transaction every month except two
  • Mass edit journal entries based on a filter

Mastodon Fund accounting, PTA sign convention, unspaced account names, reddit monetisation..

Matrix chat Tools for investment tracking, description/payee/note semantics, tag checking, arithmetic amount expressions, Money Manager EX, CSV conversion situations, categorising tools, utf-8 encoding, timedot budgets, tax preparation, if tables, payee cleanup..

Quotes

  • Thank you for writing hledger (and -ui and -web), it's wonderful work. --Michael Iles

  • Use physical order to create mental order.


This Week In Hledger 2024-02-16

sm Not much time for hledger hacking this week; support and doc tweaks, and an updated example CSV rules file for Daedalus (Cardano wallet software).

Robert Nielsen On this week's hledgerfan post, I show how the "include" statement solved a problem for running an hledger command over several data files: https://hledgerfan.com/include-was-the-key

Misc

Matrix chat Accounts vs tags, --serve-api and -X, balance assertion order, hledger.org tls cert, a visualisation of hledger compile times, stateful CSV conversion, chatGPT..

Mail list

  • Balancing "equity:conversion" split across postings.
  • How to set the opening balance in a foreign currency account?

Reddit

  • 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

Mastodon

  • A :hledger: icon is now available on fosstodon.org thanks to @nobodyinperson and @mike.

Quotes

I just discovered hledger about a month ago after being fed up with YNAB's costs and issues. I love hledger. It's amazing. Thank you for all your hard work! --megagram

Work smoothly and steadily. Don't rush.


This Week In Hledger 2024-02-08

sm I worked on refactoring and debugging the budget report for https://github.com/simonmichael/hledger/issues/2071. Also, tweaked close tags, docs, and added to the PTA FAQ: https://plaintextaccounting.org/FAQ#where-are-debits-and-credits-why-are-some-balances-negative. I shared this time report that I like:

# what happened (this week by default ?) Show simplified time categories, largest first, grouping some similar subaccounts together.
@twhat *ARGS:
    hledger -f "$TIMELOG" bal -b thisweek -e tomorrow -WATS -2 --alias '/.*:(res|adm|it)\b/=_:\1' "$@"

Robert Nielsen In this week's post, I share my R program for generating random ledger/hledger transactions. Useful if you want to have some dummy transactions for showing how some feature of hledger works without having to use real data. https://hledgerfan.com/the-code-to-generate-random-hledger-transactions

Matrix chat CSV conversion, bookkeeping situations, mixing balances and changes in a report, include vs -f, tracking saving goals, amazon prime fees, paisa..

Mail list Foreign currency accounting, adding custom tags with close, multiple postings vs multiple transactions..

Reddit

  • Need help with tracking a goal
  • Exporting Paisa interactive visualizations
  • [hledger] How do I get a specific tag column in register report
  • Do you guys enter every transaction manually
  • Thoughts on Beancount and PTA
  • Is it possible to track mileage/mpg with ledger?
  • Can I use ledger for time tracking and conversion from hours to USD?

Mastodon ..crickets..

Misc

Zegnat fixed line breaks in error messages displayed in hledger-web, making them much clearer: https://github.com/simonmichael/hledger/pull/2163

reesmichael1 made ssv: and tsv: work as file path prefixes, like csv:: https://github.com/simonmichael/hledger/pull/2165

jmtd made a dark theme for hledger-ui, testers welcome: https://github.com/simonmichael/hledger/pull/2167

brplot is a fast plotting tool for command line or wasm: https://news.ycombinator.com/item?id=39319191

Quotes

If you know how to get your transactions downloaded from your bank and don't ever want to leave the command line, hledger is great for you -- I've been using hledger since 2019 and love every minute of it.


This Week In Hledger 2024-02-02

sm I cleaned up issue labels, updated the ISSUES page, refined the user pain score, worked on an issues dashboard prototype, tweaked the tags on close's new --assert/--retain modes, and shipped hledger 1.32.3.

Robert Nielsen Working program to generate fake hledger transactions

Matrix chat topics CSV rules, --pivot, queries, -f and glob patterns, QA and bug stats, close, 1.32.3 release, forecasting, rewriting account names, decimal correctness, VAT, mixing changes and balances in a report, tracking subscribers

Mail list 1.32.3 release, foreign currency accounting

Reddit

  • Technical Debt
  • Is this possible with hledger: tracking ACB of RSUs in foreign currency and deemed dispositions when transferring to registered investment accounts?
  • Correct way to track divorce / partner withdrawal in PTA
  • Hledger amount comparison

Quotes

  • SM: too.. many.. bugs..

  • Aankhen: I’m not encountering hledger bugs. It’s just one of the most solid things I’ve ever used.


This Week In Hledger 2024-01-26

sm: Heavy work on

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

Quotes

  • hledger has a decent front-end web UI, it runs as a local server on your machine by default. The filter and searching on this view is quite good, and visualization is decent.

  • With version control and deterministic reports, you can be late, slow, make mistakes, but still keep moving incrementally towards accuracy and clarity.


This Week In Hledger 2024-01-19

sm I updated issue tracker labels and ISSUES and REGRESSIONS pages. On plaintextaccounting.org, I added tables of contents on all pages, tightened bullet lists, ran a link checker and fixed broken links, consolidated to a single more prominent PTA FAQ, and wrote a new "How to organise files" answer.

Robert Nielsen After much procrastination, I finally do the actual changes I had planned for my hledger data. Sed was the tool of choice, and you can read about it at https://hledgerfan.com/first-step-in-closing-out-2023.

Matrix chat docs, csv rules, reports, nix, flycheck-hledger error regions, lot tracking, paycheck entries, annextimelog, tags, paypal, invoicing entries, matrix clients, balance assertions, pivoting, file organisation, receivables, account types

Mastodon 4-year regressions stats

Reddit

  • Tracking an asset like stocks and their growth
  • Protecting financial data with PTA
  • hledger-flow year specific rules file
  • hledger: import from source with different decimal marks
  • Any scripts/documented methods for importing nYNAB (web) data into PTA?

Misc hamess has annotated the new features in version 1.32's manuals.

Quotes

Whoa this is amazing. And really easy!

I love hledger and have a few scripts to convert downloaded CSVs from various institutions into the appropriate format and dedupe any overlap if necessary. Once a month I download a bunch of statements, run a script and have all of the data available to me.


This Week In Hledger 2024-01-12

sm: This week I worked mostly on support and thinking/writing about improvements to close and the new file/periodic files workflow. I introduced some new terminology to distinguish starting/ending files vs opening/closing accounts, which are unrelated events. I added new --assert and --assign modes to close and clarified a problem with using balance assignments for starting balances. I reviewed status and proposed some changes at Better UX around starting and ending files ? #2151 - please take a look and give your thoughts if you're interested in this.

Over on https://plaintextaccounting.org I checked and fixed the remaining broken links; and surveyed and linked all the hledger and PTA videos visible on youtube (they're fun, check them out).

nobodyinperson: TWIH: I published annextimelog v0.10.1 and it's now in a state I would cautiously call useable for something practical as one can now record, delete and also edit logged time periods. It's a cli git-annex based time tracker focusing on conflict-free syncing between devices and flexible tagging. It has timeclock output so can be piped into hledger, e.g.

atl ls -a project=hledger -O timeclock | hledger -f timeclock:- bal -D

https://pypi.org/project/annextimelog

Robert Nielsen: Getting ledger-mode to work again!

Matrix chat topics: CSV rules , Downloading, workflows , MCC codes , PTA videos , PTA site link checking , nix , just , cost basis , emacs & VS code , annextimelog , reporting , SM's time and tasks dashboard

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 ?

Reddit:

  • [hledger] Year Closing
  • How to handle currency exchange via different accounts?
  • Regexp in rules file

Mastodon:

  • Shared expenses bookkeeping
  • just
  • flycheck-hledger
  • importing from your forecast

Open Source Collective, our fiscal host for project funds on Open Collective, may reduce transaction fees: between 15th January and 15th April, you'll see a platform tip request on every new contribution on projects hosted by Open Source Collective. If the trial is a success Open Source Collective will lower our fee on contributions through opencollective.com, making donations more cost-effective for our Collectives.

Quotes

  • each time I get the TWIH ping I get reminded to use hledger again :D But it only shows me how bad I am at managing my money 🙈😬😅

  • hledger is so awesome. I just had to do my entire accounting for the last 1½yrs (because I'm a lazy slob) and thanks to hledger's rule files, it took just one evening.


This Week In Hledger 2024-01-05

sm: I released hledger 1.32.2, which fixes bugs and adds hledger-ui on Windows.

In master I worked on

  • fix check tags to ignore modified, _modified tags automatically #2148
  • fix add,import,web: really avoid filenames ending with . on Windows #1056
  • improve GHC 9.8 support
  • add --assert and --assign modes to close, eg to help try alternate file migration workflows.

In docs:

  • RELEASING: process updates; changelogs are now updated only in release branch
  • REGRESSIONS: split the bounty between finder and fixer
  • sponsors: improve Open Collective tiers, fee info, avatar display
  • add: clarify that 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/

Misc

Quotes

  • Plain-text accounting is great. I choose hledger for the accounts of a small side business that doesn't warrant hiring an accountant.

  • I love hledger and have a few scripts to convert downloaded CSVs from various institutions into the appropriate format and dedupe any overlap if necessary. Once a month I download a bunch of statements, run a script and have all of the data available to me.


This Week In Hledger 2023-12-29

sm: Fixed a tricky csv rule parsing regression #2134 and a debug logging bug.

Improved the project Justfile, improved docs. I also improved and "described" my time and task tracking dashboard; see https://hledger.org/time-and-task-dashboard.html. (Please accept Mr. Goldberg's excellent diagram until I have time to make my own.)

And the usual support and online chatter.

Robert Nielsen: How -U helped me after making an error after mistakenly entering the wrong transactions: https://hledgerfan.com/fixing-my-mistake-after-accidentally-adding-pending-transactions

Misc: Lots of reddit posts this week, including some hledger-related ones:

And also some lively discussion on the mail list, including this proposal to simplify file opening and closing balances.

Quotes: The main thing that made me switch to hledger was the ease of autogenerating transactions into a plain-text format. ... GNUCash made recurring transactions easy but made it a lot harder to generate split transactions according to fixed splits that I regularly engage with my friends/family in. With hledger I have a system of taking CSVs, classifying transactions using a stack of Python scripts and using them to generate hledger importable files. I go by hand and annotate transactions that my scripts couldn't classify/split, and then I import it into my main hledger file. I store all my inputs and intermediate imports in a git repository along with my main ledger file for auditability and to look at annotations. The ease of automation just can't be beat with PTA but it's definitely harder to use if you don't have a preference for TUI work. Ease of writing importers was a huge driving factor when I switched to hledger from GNUCash.


This Week In Hledger 2023-12-22

sm: This week I worked more on consolidating project scripts, porting make rules to just. Also participated in timeclock discussions, did some research and brainstormed next steps.

nobodyinperson: I published annextimelog v0.3.1, a git-annex based time tracker (alpha quality, bare minimum functionality), which can export its data to hledger-readable timeclock.

https://pypi.org/project/annextimelog

Robert Nielsen: On the hledger fan website I look at using desc as a timesaver, and why this matters: https://hledgerfan.com/a-better-description

Misc: ShrykeWindgrace's PR upgrading hledger-ui to the new brick and vty libs has landed; hledger-ui now runs natively on Microsoft Windows.

Quotes of the week: I’ve been using hledger for a few years. ... Every service lets you download historical data as CSV, which can be imported by hledger and turned into journal files and then into visual reports. ... It’s a bespoke setup and super nerdy and is a bit of a pain, but it’s literally the only solution that works for us.


This Week In Hledger 2023-12-15

Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with "TWIH").

sm: Bugfixing

  • Issue triage, ISSUES > Prioritising doc, dashboard, new issue template updates
  • hledger-web: fix --base-url, drop --file-url, better startup messages, improve tests, code cleanups, manual cleanups, code docs.

Robert Nielsen: Discussion of the holiday season and hledger on the hledger fan website: https://hledgerfan.com/the-holiday-season-and-hledger

Misc: budget-cli - budget-focussed, CSV-centric, typescript PTA app with a good write-up

Quotes of the week: I switched, blissfully, away from Quicken/Quickbook/Xero/all that heavy crap to very simple and elegant textfile-based accounting (ledger/hledger) and it's been the best and most flexible accounting experience I've ever had. I finally feel completely in control of my companies' books.


This Week In Hledger 2023-12-08

Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with "TWIH").

sm: Fixed a number of issues with 1.32 and released 1.32.1.

Rewrote the hledger manual's "Budget report" section, and moved some content to the website's Budgeting page.

Tried converting the bake bash scripts to just scripts.

Refactoring, issues, support.

Robert Nielsen: On the hledger fan website, this I discuss one of the most useful ledger mode commands for Emacs. Also, a book recommendation for graphic design that you need to know about: https://hledgerfan.com/upgrading-to-hledger-1-32-a-book-on-graphic-design-and-ledger-mode-clean-buffer

Quotes of the week: I’ve been researching the history of double entry bookkeeping alongside learning the basics of accounting (which is how all this started) and I think hledger and the whole universe of plain text accounting software is super cool. Just a lovely evolution of the method. Thanks for making it so welcoming and accessible


This Week In Hledger 2023-12-01

Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with "TWIH").

sm:

Dev:

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

Ops:

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

Docs:

Use cli class instead of shell for all command-line examples in hledger docs.

hledger: Amount formatting, parseability

Ledger: interoperating tips updates, more about commodity directives and balance assertions.

Videos: reorder hledger fan videos.

REGRESSIONS: cleanups, discuss real names.

Other:

Prepared change notes and released hledger 1.32 with the last quarter's changes.

I did a big cleanup on plaintextaccounting.org, which has absorbed the wiki and now supports Obsidian-style wiki links and clickable headings.

Robert Nielsen: Two things in this week's hledger fan blog. First, for beginners, some notes on reducing errors when reconciling accounts. Second, for the more advanced, a recommendation for an Awk programming book. See the details at https://hledgerfan.com/fewer-errors-in-reconciling/.

Quotes of the week:

It's the basis of my entire personal finance system.

once again, I was deeply impressed by how useful hledger is, and how thoughtful it is.


This Week In Hledger 2023-11-24

Share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat, every Friday morning (or any time, tagged with "TWIH").

sm:

Dev:

Fixes:

  • Non-print-like reports no longer add trailing decimal marks (a regression in 1.31). Clarified the policy on number formatting adjustments made in certain reports/output formats. (#2115)
  • tag: queries now work when reading CSV files. (#2114)
  • -o/--outputfile with a .json or .sql extension now properly selects those formats.

Improvements/features:

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

Docs:

hledger: added "Amount formatting, parseability", updated "Decimal marks, digit group marks", "commodity directive", "CSV > if table", "Timedot".

Ledger: added more notes on commodity directives, from chat.

Checking for errors: added tips on enforcing account lifetimes, from reddit, and on minimising diffs in reports, from mail list.

Cookbook: added "Reporting version control stats".

Over at https://plaintextaccounting.org/#articles-blog-posts I started adding blog links for 2021-2023, including 10 33 hledger-related posts from Robert Nielsen, Jonathan Dowland, Arsen Arsenović, and Zoran Zaric.

Misc:

Updated project finance scripts, regenerated the project ledger with consistent precisions in assertions, updated it with the last few months of data from Open Collective.

Robert Nielsen: On the theme of the recent Thanksgiving Day, here are three ways you can use hledger to be thankful: https://hledgerfan.com/using-hledger-for-thanksgiving/.

Quotes of the week: Have been taking care of my (new) records for almost two years now and this data is really valuable for me. Gives me very nice overview over everything and confidence that everything is alright and so on. Just looking at some graphs can make you feel really good - in terms of "I know whats happening", not necessarily in "all is well", depending on those actual numbers! 😆 --someone using Beancount


This Week In Hledger 2023-11-17

Every Friday morning (or any time, tagged with "TWIH"): share your hledger/plain text accounting user notes, dev news, achievements or experiences in the hledger matrix chat.

sm:

Dev

A new hledger user pointed out in chat out that -s/--strict mode didn't work with the import command. This exposed a bug (#2113) and led to some reworking of the API in Hledger.Read. Until now, when reading multiple files, -s/--strict checks were performed for each individual file, causing spurious failures, with import and other commands. Now strict checks are done only once, for the overall journal. Also, the import command now only updates .latest files at the end of a successful run (after successfully passing strict checks and after updating the journal file).

Post-"Precisiongeddon" followup: updating hledger-ui and hledger-web to show amounts with correct display precision.

Ops

Fixed a problem with cloudflare authentication that was preventing automatic TLS certificate renewal on hledger.org.

Clarified and documented my cloudflare tokens, and moved them to env vars so the latest caddy config could be committed.

Fixed the hledger.org webhook that was not updating the site on git push.

Enabled https for "code.hledger.org" and "site.hledger.org" short urls.

Fixed some caddy warnings, formatted the config file, added some caddy/webhook management scripts to the site makefile.

Docs

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

Misc

Time breakdown this week: 52% enhancement, 35% cleanup, 13% support.

Robert Nielsen: On the hledger fan website, I discuss using the aregister and print commands to get narrow and wider views of your data: https://hledgerfan.com/zooming-out-and-in

Quotes of the week:

Great software btw, I appreciate your work! Been poking around with PTA and hledger since I was laid off, and it's been incredible for managing/extending my runway 😁 I was a Mint/Personal Capital power user before, but this takes it to the next level. Got a new job and I've already forecasted out the whole next year, very handy and something I could never do before"

I spent some time trying to set up hledger so that I’d actually use it. With multiple accounts it really got tedious but I loved the actual double entry accounting and level of detail. My wife was blown away by the year end summary I made with it. You can see early how much went to taxes, fica etc etc.

I'm really happy how my hledger edit addon helps me quickly reconcile transactions. After CSV-importing from all different sources (phone notes, bank websites, etc.), I pile up the ridiculous amount of invoices and receipts next to me, grab one after the other and execute hledger edit amt:35.11 or hledger edit Checking desc:ALDI for example to find and edit the transaction in my $EDITOR, no matter where that txn was located originally. The other way round (going through the journal, then finding the physical invoice/receipt) turned out to be much slower.

I learned one thing: if %FIELDNAME REGEX matches against FIELDNAME in the CSV. I didn't know that! I always match against the whole line and am annoyed if the order of columns makes this weird


This Week In Hledger 2023-11-10

It's time for This Week In Hledger! Every Friday morning (or any time, tagged with "TWIH"), share your hledger/PTA user notes, dev news, achievements or experiences in the hledger matrix chat.

sm:

Dev

This week I completed the months-long yak shave that became Precisiongeddon, and it has landed in master; see https://github.com/simonmichael/hledger/pull/2111 for details. Heads up: this can change default precisions shown especially by cost and value reports - all for the better hopefully, but any pre-release testing is welcome. There's a linux binary at https://github.com/simonmichael/hledger/actions/runs/6804488282.

CI (continuous integration) workflows on github have been optimised a bit:

  • Scheduled weekly builds have been disabled, as they were propagating to forks and running wastefully there in some cases.
  • Some repeated rebuilding of the hledger-lib and hledger packages that seems unnecessary has been stopped.
  • hledger-ui no longer builds its modules twice.
  • Haddock testing has been moved to the release workflows to save time.

Fixed:

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

Docs

As part of Precisiongeddon, I started expanding hledger's "code docs", in the Hledger module's haddock. I spent time learning how to navigate Haddock and producing a fast-feedback authoring workflow (make haddock-watch). And documented a bunch of "jargon" terms, and how we handle precision and display styles.

The "Regular expressions" manual section has been rewritten and now has examples: https://hledger.org/dev/hledger.html#regular-expressions.

Unmentioned last week: I added a News page on hledger.org as a stable home for This Week In Hledger and other news updates.

Misc

Some notable commits:

  • feat: cli: Add tsv output (#869) (Peter Sagerson)
  • feat: import: interpolate regex matches in field templates (#2009) (Jonathan Dowland)

There's new interest and design discussion for referencing the matched account name in auto postings, and I have added to lukasbestle's $20 bounty to make it $100: https://github.com/simonmichael/hledger/issues/1975

Robert Nielsen: For more advanced hledger aficionados, here is a diagram showing just about every possible part of an hledger transaction: https://hledgerfan.com/almost-everything-you-wanted-to-know-about-hledger-transactions.

Quotes of the week:

Overall very happy with hledger so far btw!

And thanks for the amazing tool. Migrating to hledger has been one of the most satisfying bits of digital homesteading I’ve done.


This Week In Hledger 2023-11-03

sm:

Dev

  • tests: add outshine headings for folding/readability (in Emacs), readme updates, include .test files in TAGS, clean up ledger-compat tests
  • tools: bake dbgstrs - print a bunch of hledger debug messages for review
  • tools: ghci :rmain - like :main, but reloads first, useful eg when changing --debug level
  • WIP: imp: prices: fixes, clarify/improve semantics
  • WIP: precisiongeddon: precision/rounding cleanups/fixes/tests/docs, too intricate to describe

Continuing heavy work on consistent and optimal precision handling, and all the related yaks which keep showing up.

Seen

  • https://www.chvp.be/blog/nanoledger/ nice blog post introducing nanoledger android app

Robert Nielsen: Beginner's corner: What are the parts of a basic hledger transaction? Two resources for learners: https://hledgerfan.com/beginners-corner-a-basic-hledger-transaction


This Week In Hledger 2023-10-27

sm:

Dev

  • demo: avoid a bug in asciinema 2.3.0, and improve the error message when asciinema fails (#2094)
  • web: fix broken "File format help" link in the edit form (#2103)
  • web: access control UX cleanups: replace --capabilities and --capabilities-header with --allow, and validate it before starting the app (#834)
  • web: refactor permission checking
  • web: sandstorm web app cleanups; rename/reorder roles & permissions
  • WIP: testing and improving amount style and precision handling in cost/value reports

Ops

  • Set up a self-hosted Sandstorm server and learned how to configure it.
  • Set up a public hledger-web instance in it: sandbox.hledger.org. Unlike https://demo.hledger.org, and until the spammers find it, sandbox is fully writable - use it as a pastebin for examples, eg.

Docs

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

Examples

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

Misc

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

Seen

chvp: A new Android app, NanoLedger, was added to F-droid to facilitate adding transactions on the fly. Payees, notes and accounts can all be autocompleted. Give it a try at https://f-droid.org/en/packages/be.chvp.nanoledger/

Robert Nielsen: Why I created the hledger fan YouTube channel: https://hledgerfan.com/why-i-created-the-hledger-fan-youtube-channel


This Week In Hledger 2023-10-20

G'day @room. It's time for This Week In Hledger ! Every Friday morning, if you have any user or dev news or experiences you'd like to share, post them in the hledger matrix chat (https://matrix.to/#/#hledger:matrix.org) as a message prefixed by "TWIH:". Markdown formatting and edits are welcome. On Saturday I'll publish on the hledger mail list and on mastodon.

sm: Changes merged this week include:

Dev

  • print: add --round option for more control of precisions (#2085)
  • balance-assigned amounts affect commodity styles again (#2091, regression in 1.30)
  • timedot: parse unitful quantities more accurately (#2096)
  • Jacob Weisz has updated the hledger-web app on Sandstorm (The amazing https://sandstorm.io cloud app platform that lets you set up free private hledger-web instances with a few clicks, check it out!)

Ops

  • renamed main CI workflow and branch to "ci"
  • updated and committed hledger.org's caddy config and short urls (redirects)
  • there's now an easy short url for trying out the hledger-web app on Sandstorm: https://sandstorm.hledger.org.

Docs

  • moved regression bounty info to https://hledger.org/REGRESSIONS.html
  • hledger manual > Journal > Commodity display style: rewritten
  • hledger manual > aregister, hledger-ui manual > Register screen: note how separately-dated postings get combined

Examples

  • justfile: forecast-import: show but don't consume future-dated forecast transactions

Robert Nielsen: https://hledgerfan.com/a-not-so-capital-idea-for-saving-time/ describes a timesaving tip for command line users. Specifically, when typing one of your account names, hledger "forgives" you if you don't match the capitalization of an account name.


This Week In Hledger 2023-10-13

[hey there @room - I'd like to get a regular This Week In Hledger started, along the lines of This Week in Matrix (TWIM) ... Having a regular news heartbeat like this, even a small one, will help build our community and momentum.]

Welcome to This Week In Hledger ! Every Friday morning, if you have any user or dev news or experiences you'd like to share, post them in the hledger matrix chat room as a message prefixed by "TWIH:". Markdown formatting welcome, edits welcome. On Saturday I'll publish on the hledger mail list and on mastodon. (Inspired by This Week In Matrix).

sm:

Merged

WIP

  • more control of display precision with print
  • more consistent precision handling in general
  • displaying zeros with consistent commodity symbol, precision etc. when possible
  • bug wrangling

Docs

danielchappell:

What I like about the data format

  • Double Entry accounting in human readable files, the journal files.
  • Balance assignments (to check in on my cash-wallet occasionally)
  • Virtual transactions (to track funds)

What I like about HLedger specifically

What I need to do my homework on

  • The limitations of balance assertions in HLedger
  • study haskell

What makes me cry about HLedger

Project accounting

Some ways to track small business/freelancer activity - orders, budgets, invoices, payments..

Accrual method

Revenue is declared when work is performed:

; budget:* - virtual accounts tracking what customers have committed
; to pay for various things. Should not go below 0.
2017/10/30 Order from CUSTOMER (order id) 
    (budget:CUSTOMER:PROJECT_ID:pos1)                       1000
    (budget:CUSTOMER:PROJECT_ID:pos2)                       3000
     
; some work was done on pos1 and pos2, invoice for it.
; Using accrual accounting method
; (revenue is declared when work is done, ~= when invoiced)
2017/10/31 Invoice (invoice id) - (PROJECT_ID)
    (budget:CUSTOMER:PROJECT_ID:pos1)                       -500  ; update project budget
    (budget:CUSTOMER:PROJECT_ID:pos2)                      -1000
    assets:receivable:CUSTOMER:PROJECT_ID:pos1               500
    assets:receivable:CUSTOMER:PROJECT_ID:pos2              1000
    revenues:CUSTOMER
    (liabilities:tax:federal)                               -150  ; note tax due, eg 15% of revenue

; a customer payment is received
2017/11/15 Payment for INVOICE_ID
    assets:receivable:CUSTOMER:PROJECT_ID:pos1              -500
    assets:receivable:CUSTOMER:PROJECT_ID:pos2             -1000
    assets:bank:checking

; make a tax payment
2018/4/15 Pay taxes due from 2017
    liabilities:tax:federal                                 5000
    assets:bank:checking

Cash method

Revenue is declared when payment is received:

2017/10/30 Order from CUSTOMER (order id) 
    (budget:CUSTOMER:PROJECT_ID:pos1)                       1000
    (budget:CUSTOMER:PROJECT_ID:pos2)                       3000

; record an invoice sent. Not a real transaction in cash accounting,
; but we can balance it with the project budget as shown:
2017/10/31 Invoice (invoice id) - (PROJECT_ID)
    budget:CUSTOMER:PROJECT_ID:pos1                         -500
    assets:receivable:CUSTOMER:PROJECT_ID:pos1               500
    budget:CUSTOMER:PROJECT_ID:pos2                        -1000
    assets:receivable:CUSTOMER:PROJECT_ID:pos2              1000

; receive payment. Cash basis, so revenue declared here.
2017/11/15 Payment for INVOICE_ID
    (assets:receivable:CUSTOMER:PROJECT_ID:pos1)            -500
    (assets:receivable:CUSTOMER:PROJECT_ID:pos2)           -1000
    revenues:CUSTOMER                                      -1500
    (liabilities:tax:federal)                               -150  ; note tax due, eg 15% of revenue
    assets:bank:checking

; make a tax payment
2018/4/15 Pay taxes due from 2017
    liabilities:tax:federal                                 5000
    assets:bank:checking

2 minute quick start

One of the introductions to hledger; for others, see Get Started.

Here is a sequence of examples, focussed on command-line usage:

$ brew install hledger    # or apt, choco, but check Install for freshness
$ cat >main.journal    # record a transaction manually from command line
2022-01-01 opening balances as of this date
    assets:bank:checking           $1000  ; This is a comment.
    assets:bank:savings            $2000  ; 2+ spaces are required before each amount.
    assets:cash                     $100
    liabilities:creditcard          $-50
    equity:opening/closing balances
^D
$ export LEDGER_FILE=main.journal    # use this file by default
$ echo 'export LEDGER_FILE=main.journal' >>~/.profile    # and in future sessions
$ hledger add    # record a transaction interactively
Adding transactions to journal file main.journal
Any command line arguments will be used as defaults.
Use tab key to complete, readline keys to edit, enter to accept defaults.
An optional (CODE) may follow transaction dates.
An optional ; COMMENT may follow descriptions or amounts.
If you make a mistake, enter < at any prompt to go one step backward.
To end a transaction, enter . when prompted.
To quit, enter . at a date prompt or press control-d or control-c.
Date [2022-02-08]: 2/15
Description: market
Account 1: expenses:food
Amount  1: $50
Account 2: assets:cash
Amount  2 [$-50]: 
Account 3 (or . or enter to finish this transaction): 
2022-02-15 market
    expenses:food             $50
    assets:cash              $-50

Save this transaction to the journal ? [y]: 
Saved.
Starting the next transaction (. or ctrl-D/ctrl-C to quit)
Date [2022-02-15]: 
$ hledger stats    # show journal statistics
Main file                : main.journal
Included files           : 
Transactions span        : 2022-01-01 to 2022-02-16 (46 days)
Last transaction         : 2022-02-15 (7 days from now)
Transactions             : 2 (0.0 per day)
Transactions last 30 days: 0 (0.0 per day)
Transactions last 7 days : 0 (0.0 per day)
Payees/descriptions      : 1
Accounts                 : 6 (depth 3)
Commodities              : 1 ($)
Market prices            : 0 ()

Run time (throughput)    : 0.04s (47 txns/s)
$ hledger bal --monthly    # show account balance changes each month
Balance changes in 2022-01-01..2022-02-28:

                                 ||    Jan   Feb 
=================================++==============
 assets:bank:checking            ||  $1000     0 
 assets:bank:savings             ||  $2000     0 
 assets:cash                     ||   $100  $-50 
 equity:opening/closing balances || $-3050     0 
 expenses:food                   ||      0   $50 
 liabilities:creditcard          ||   $-50     0 
---------------------------------++--------------
                                 ||      0     0 
$ cat >checking.csv    # make some CSV data, as if downloaded from a bank
"Date","Note","Amount"
"2022/2/01","GOODWORKS CORP","-1000.00"
"2022/2/22","PROPERTY MGMT CO","500.00"
"2022/2/23","ATM WITHDRAWAL","-100.00"
^D
$ cat >checking.csv.rules    # and a rules file to help hledger read it
skip 1
fields date, description, amount
account1 assets:bank:checking
currency $
amount   -%amount

if GOODWORKS
 account2 income:salary

if PROPERTY
 account2 expenses:rent

if ATM WITHDRAWAL
 account2 assets:cash
^D
$ hledger import checking.csv    # import CSV records as new journal entries
imported 2 new transactions from checking.csv
$ hledger import checking.csv    # records already seen are ignored; cf --dry-run
no new transactions found in checking.csv
$ hledger print date:202202   # show transactions in february
2022-02-01 GOODWORKS CORP
    assets:bank:checking           $1000
    income:salary                 $-1000

2022-02-15 market
    expenses:food             $50
    assets:cash              $-50

2022-02-22 PROPERTY MGMT CO
    assets:bank:checking           $-500
    expenses:rent                   $500

2022-02-23 ATM WITHDRAWAL
    assets:bank:checking           $-100
    assets:cash                     $100

$ hledger is -M    # show a monthly income statement (profit & loss report)
Income Statement 2022-01-01..2022-02-28

               || Jan    Feb 
===============++============
 Revenues      ||            
---------------++------------
 income:salary ||   0  $1000 
---------------++------------
               ||   0  $1000 
===============++============
 Expenses      ||            
---------------++------------
 expenses:food ||   0    $50 
 expenses:rent ||   0   $500 
---------------++------------
               ||   0   $550 
===============++============
 Net:          ||   0   $450 
$ hledger bs -M --tree    # show monthly asset and liability balances
Balance Sheet 2022-01-31..2022-02-28

                        || 2022-01-31  2022-02-28 
========================++========================
 Assets                 ||                        
------------------------++------------------------
 assets                 ||      $3100       $3550 
   bank                 ||      $3000       $3400 
     checking           ||      $1000       $1400 
     savings            ||      $2000       $2000 
   cash                 ||       $100        $150 
------------------------++------------------------
                        ||      $3100       $3550 
========================++========================
 Liabilities            ||                        
------------------------++------------------------
 liabilities:creditcard ||        $50         $50 
------------------------++------------------------
                        ||        $50         $50 
========================++========================
 Net:                   ||      $3050       $3500 
$ hledger areg checking    # show checking's transactions and running balance
Transactions in assets:bank:checking and subaccounts:
2022-01-01 opening balances    as:ba:savings, as..         $1000         $1000
2022-02-01 GOODWORKS CORP      in:salary                   $1000         $2000
2022-02-22 PROPERTY MGMT CO    ex:rent                     $-500         $1500
2022-02-23 ATM WITHDRAWAL      as:cash                     $-100         $1400
$ hledger-ui --forecast   # start the terminal UI (except on Windows)

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

$ hledger-web    # start the web UI

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

5 minute quick start

One of the introductions to hledger; for others, see Get Started.

What is it ?

hledger: free GPLv3+ accounting software for linux, mac, windows, web, etc.

How do I use it ?

At the start:

  1. Install one or more of the hledger tools
  2. Set up a journal, and maybe version control

On a regular basis (eg daily, can be <5m):

  1. Enter transactions manually and/or
  2. Import transactions from banks' CSV
  3. Reconcile to catch mistakes

Whenever you like:

  1. Run reports to answer questions and gain insight
  2. Refine account names, CSV rules etc. to improve your reports and efficiency.

Knowing some double entry accounting will help you get the most from hledger, but you can do fine just by following the examples below. You'll find your bookkeeping/accounting skills improve naturally (and help is available).

Install

Quickly: install hledger binaries, using your system package manager or our Github binaries.

Or slowly: build hledger from source, using hledger-install.sh or stack or cabal.

Set up a journal

The journal file is a plain text file where transactions are recorded. By default it is ~/.hledger.journal, and the add command or web add form described below will create it automatically, so actually you don't need to do anything here.

But here are some common changes people make sooner or later, so why not now:

  • A dedicated folder, to consolidate financial files and make version control and backups easier:

    $ mkdir ~/finance
    $ cd ~/finance
    
  • A separate journal file for each year, for performance and data compartmentalisation:

    $ touch 2023.journal
    
  • A LEDGER_FILE environment variable, so you won't have to type "-f ~/finance/2023.journal" with every command:

    $ echo "export LEDGER_FILE=~/finance/2023.journal" >> ~/.bashrc
    $ source ~/.bashrc
    

    Or if environment variables annoy you, symbolic-link the file to ~/.hledger.journal:

    $ ln -s ~/finance/2023.journal ~/.hledger.journal
    
  • Some optional directives, useful especially with non-english account names:

    $ cat > 2023.journal
    
    ; Declare the five top level accounts in your preferred language/capitalisation;
    ; their types (to make reports like bs and is work); and their display order.
    ; Write at least 2 spaces before the ; comments.
    account assets       ; type:A, money you own. 2+ spaces are required before the ;.
    account liabilities  ; type:L, money you owe to others
    account equity       ; type:E, equal to A - L (not used much in personal finance)
    account revenues     ; type:R, revenue/income categories
    account expenses     ; type:X, expense categories
    
    ; Declare commodities/currencies and their decimal mark, digit grouping,
    ; number of decimal places..
    commodity $1000.00
    commodity 1.000,00 EUR
    
    <CTRL-D> (paste the command & text above into the terminal, then press control-d)
    
  • Version control, for tracking changes:

    $ git init
    $ git add 2023.journal
    $ git commit 2023.journal -m 'start 2023 journal'
    
  • Remember to also keep backups.

Enter transactions

Recording transactions manually may sound tedious, but with a good text editor or other data entry tool it can be fast. It also provides greatest financial awareness. Some people enter everything by hand for this reason.

Run the add command for assisted data entry in the terminal (tutorial):

$ hledger add
...
Date [2023-03-10]: ...

Or run hledger-web and when the web browser opens, press a to add (tutorial):

$ hledger-web
...
Opening web browser...

Or using a text editor, add transactions to your journal file like so:

2023-01-01 opening balances on january 1st
    assets:checking         $1000  ; a posting, increasing assets:checking's balance by $1000
    assets:cash              $100  ; write at least 2 spaces between account name and amount
    liabilities                $0
    equity                 $-1100  ; each transaction must sum to zero

2023-03-05 client payment
    assets:checking         $2000
    revenues:consulting    $-2000  ; revenues/liabilities/equity normally appear negative

2023-03-20 Sprouts
    expenses:food:groceries  $100
    assets:cash               $40
    assets:checking                ; a missing amount will be inferred ($-140 here)

As shown above, make the first transaction a dummy one that sets the opening balances of your asset & liability accounts on some start date. hledger will show accurate real-world account balances from this date onward, as long as you record the subsequent transactions.

To make things easy on yourself, you can pick a very recent start date, like today or last monday. Prioritise recording the transactions that happen after this date. (Tip: the more often you do this, the easier it is.)

Then, as your time and financial records and desire for historical reports allow, you can add older transactions. As you do, you'll need to adjust the opening balances transaction, moving it back in time. Perhaps focus on one account at a time, each with its own opening balances transaction if necessary.

Import transactions

Import means 1. convert transaction data from some other format (usually a downloaded CSV file) and 2. save any new transactions to the main journal file. It is often possible to automate this, perhaps to the point of a nightly cron job and no manual data entry at all. This is convenient but costs some financial awareness.

Download one or more CSV files containing transaction info, then create a csv rules file for each. Eg if SomeBank.csv looks like:

"Date","Note","Amount"
"2023/3/22","DEPOSIT","50.00"
"2023/3/23","ATM WITHDRAWAL","-10.00"

Create SomeBank.csv.rules containing rules like:

skip 1
fields date, description, amount
currency $
account1 assets:checking
account2 expenses:misc
if DEPOSIT
 account2 revenues:misc
if ATM WITHDRAWAL
 account2 assets:cash

Check the csv conversion looks ok:

$ hledger -f SomeBank.csv print
2023-03-22 DEPOSIT
    assets:checking          $50.00
    revenues:misc           $-50.00

2023-03-23 ATM WITHDRAWAL
    assets:checking         $-10.00
    assets:cash              $10.00

You can run reports directly from the csv, but I like to import the new transactions into the main journal, keeping things in one place. The import command ignores csv records it has seen before, saving the latest dates in .latest.SomeBank.csv. This works for most csv files - you can try a dry run first:

$ hledger import *.csv --dry-run
; would import 2 new transactions from SomeBank.csv:

2023-03-22 DEPOSIT
    assets:checking          $50.00
    revenues:misc           $-50.00

2023-03-23 ATM WITHDRAWAL
    assets:checking         $-10.00
    assets:cash              $10.00

$ hledger import *.csv 
imported 2 new transactions from SomeBank.csv
$ hledger import *.csv
no new transactions found in SomeBank.csv

Now to commit the new rules file and changed journal file:

$ git add SomeBank.csv.rules
$ git commit -m 'SomeBank csv rules' SomeBank.csv.rules
$ git commit -m 'txns' 2023.journal

In the above workflow, the journal file is permanent and downloaded csv files are temporary. Some folks (Full-fledged hledger, hledger-flow) prefer to instead commit all csv files and regenerate the journal file.

Reconcile

After entering or importing transactions, it's important to check for mistakes (yours or others'), by comparing your reports with reality - your wallet, statements, online balances etc. See Reconciling.

Run reports

$ hledger accounts   # account names declared and used, as a list
assets
assets:cash
assets:checking
liabilities
equity
revenues
revenues:consulting
expenses
expenses:food:groceries

$ hledger accounts --tree   # accounts are actually a hierarchy
assets
  cash
  checking
equity
expenses
  food
    groceries
liabilities
revenues
  consulting

$ hledger balancesheet    # what do I own and owe ?
$ hledger bs              # short form
Balance Sheet 2023-03-20

                 || 2023-03-20 
=================++============
 Assets          ||            
-----------------++------------
 assets:cash     ||       $140 
 assets:checking ||      $2860 
-----------------++------------
                 ||      $3000 
=================++============
 Liabilities     ||            
-----------------++------------
-----------------++------------
                 ||            
=================++============
 Net:            ||      $3000 

$ hledger aregister --forecast checking   # or: hledger register checking
Transactions in assets:checking and subaccounts:
2023-01-01 opening balances ..  as:cash, liabiliti..         $1000         $1000
2023-03-05 client payment       re:consulting                $2000         $3000
2023-03-20 Sprouts              ex:fo:groceries, a..         $-140         $2860

$ hledger incomestatement --monthly --depth 2    # where is it coming from and going to ?
$ hledger is -M -2                               # short form
Income Statement 2023Q1

                     || Jan  Feb    Mar 
=====================++=================
 Revenues            ||                 
---------------------++-----------------
 revenues:consulting ||   0    0  $2000 
---------------------++-----------------
                     ||   0    0  $2000 
=====================++=================
 Expenses            ||                 
---------------------++-----------------
 expenses:food       ||   0    0   $100 
---------------------++-----------------
                     ||   0    0   $100 
=====================++=================
 Net:                ||   0    0  $1900 

$ hledger                         # show commands

$ hledger --help                  # show general options

$ hledger --man                   # show hledger's man page

$ hledger --info                  # show hledger's Info manual

$ hledger is --help               # show incomestatement's options and docs

$ hledger is --man                # show incomestatement in man page

$ hledger is --info               # show incomestatement's Info page

$ hledger help                    # show hledger docs in best available viewer

$ hledger help incomestatement    # show incomestatement docs in best available viewer

$ hledger-ui                      # start TUI

$ hledger-web                     # start WUI in default browser

For more detail, see:

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

Fixes

  • add, import, web: On MS Windows, don't allow writing to files whose name ends with a period, since it can cause data loss; raise an error instead. I made this change in hledger 1.15 (2019), but it never worked; now it does. #1056

  • balance --budget: The budget report in tree mode was omitting parent accounts with no actual or goal amounts and a single child, instead of showing them as a prefix of the child's name. Now it always shows them, on a line of their own (a bit like --no-elide). It's not a perfect fix, but the budget report code is twisty. #2071

  • check tags: The 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-1.6.19.0+ to avoid any vulnerabilities on Windows from HSEC-2024-0003.

Features

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

Improvements

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

Docs

  • 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

Scripts/addons

  • hledger-bar, hledger-simplebal: shellcheck fixes, cleanups (Colin Dean)

  • hledger-bar: Fix an error when NO_COLOR is not defined #2159. Also, it's now more compliant with the no-color.org spec:

    Command-line software which adds ANSI color to its output by default should check for a NO_COLOR environment variable that, when present and not an empty string (regardless of its value), prevents the addition of ANSI color.

    so one can now temporarily override $NO_COLOR=1 in the environment by setting it empty: NO_COLOR= hledger ...

  • hledger-txnsbycat: added

API

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

hledger-ui 1.33

Fixes

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

Features

  • Add a dark theme. (Jonathan Dowland)

Improvements

  • Allow building with GHC 9.8.

  • Require safe >=0.3.20.

hledger-web 1.33

Fixes

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

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

Improvements

  • Zero amounts are now shown with their commodity symbol. This was mainly to make the sidebar more informative, but also affects and hopefully helps amounts displayed elsewhere. #2140

  • Amounts in the sidebar now also have the amount HTML class.

  • Allow building with GHC 9.8.

  • Require safe >=0.3.20.

Docs

  • 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

Misc

  • Apple ARM binaries are now included in github releases.

Docs

  • REGRESSIONS: we now split the bounty between finder and fixer
  • move Developer docs, MOCKUPS, investment-accounting-features to main repo
  • merge LINKS into dev docs page; cleanup
  • drop unused BACKLOG, TODO pages

credits 1.33

Simon Michael, Jonathan Dowland, Ilja Kocken, Colin Dean, Dmitry Astapov, Vekhir, ShrykeWindgrace, Martijn van der Ven, Michael Rees, aragaer, hamzashezad.

2024-01-28 hledger-1.32.3

hledger 1.32.3

Fixes

  • A performance slowdown since 1.29, especially noticeable with many accounts and transactions, has been fixed. #2153

  • Balance assertions involving mixed-cost balances are checked correctly again (a regression in 1.30). #2150

  • import --catchup works again (a regression in 1.32). #2156

  • --anon is now a deprecated hidden flag that raises an error, but is still usable as --obfuscate (also hidden). #2133

  • Balance assertion error messages are clearer, and show the diff again.

hledger-ui 1.32.3

  • Use hledger-1.32.3

  • Allow vty 6.2, brick 2.3

hledger-web 1.32.3

  • Use hledger-1.32.3

project changes 1.32.3

  • bin/hledger-bar: Fix an error when NO_COLOR is not defined; allow color when NO_COLOR is defined but empty, per no-color spec; and fix shellcheck warnings. #2159 (Colin Dean, Simon Michael)

  • bin/hledger-simplebal: Fix shellcheck warnings. (Colin Dean)

credits 1.32.3

Simon Michael, Colin Dean.

2023-12-31 hledger-1.32.2

hledger 1.32.2

Fixes

  • In CSV field assignments, %FIELD interpolation and \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.

Improvements

  • Allow megaparsec 9.6

Docs

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

hledger-ui 1.32.2

Features

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

Improvements

  • Use Notepad as default editor on Windows (ShrykeWindgrace)

  • Allow brick 2.2 (Vekhir)

  • Allow megaparsec 9.6

hledger-web 1.32.2

Fixes

  • 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

Improvements

  • Allow megaparsec 9.6

  • hledger-web's tests now respect and can test command line options.

  • hledger-web's tests now run the app at 127.0.0.1 and port 5000, rather than "any of our IPv4 or IPv6 addresses" and 3000.

2023-12-07 hledger-1.32.1

hledger 1.32.1

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

Features

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

Improvements

  • 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
    (default)nonenonenonenone
    --round=softsoftnonesoftnone
    --round=hardhardnonehardnone
    --round=allhardhardhardhard
  • 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.

Fixes

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

Docs

  • New:

    • Amount formatting, parseability
    • Started new code docs for developers, based in the Hledger module's haddock
  • Updated:

    • aregister
    • commodity directive
    • Commodity display style
    • if table
    • Decimal marks, digit group marks
    • Regular expressions
    • Timedot

hledger-ui 1.32

Fixes

  • The V key now preserves the valuation mode specified at the command line, if any. (#2084)

  • The hledger-ui package no longer wastefully builds its modules twice.

hledger-web 1.32

Features

  • The hledger-web app on the Sandstorm cloud platform has been updated to a recent version (Jacob Weisz, #2102), and now uses Sandstorm's access control. (Jakub Zárybnický, #821)

Improvements

  • The --capabilities and --capabilities-header options have been replaced with an easier --allow=view|add|edit|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)

Fixes

  • The app can now serve on address 0.0.0.0 (exposing it on all interfaces), which previously didn't work. (#2099) (Philipp Klocke)

  • The broken "File format help" link in the edit form has been fixed. (#2103)

project changes 1.32

Scripts/addons

  • hledger-install.sh: replaced hledger-stockquotes with pricehist

  • added gsheet-csv.hs: fetch a google sheet as CSV

  • added hledger-report1: an example custom compound report, with haskell and bash versions

  • justfile: updated import, time report scripts

Examples

  • New:

    • Fidelity CSV rules
  • Updated:

    • roi-unrealised.ledger (Charlie Ambrose)

Docs

  • New:

    • Started a weekly This Week In Hledger news post, inspired by Matrix.
    • There's now a News page, for This Week In Hledger etc.
    • hledgermatic, an up-to-date, simple journal-first workflow
    • How to record journal entries: added
    • Reporting version control stats: added
    • Moved regression bounty info from the issue tracker to Developer docs > REGRESSIONS.
  • Updated:

    • Checking for errors
    • Common workflows
    • Ledger
    • Simon's old setup
    • Videos
    • All docs now use the cli class instead of shell for command-line examples, avoiding inaccurate highlighting.

Infrastructure

  • hledger.org website:

    • Fixed the webhook that was not updating the site on git push.

    • Fixed a problem with cloudflare authentication that was preventing automatic TLS certificate renewal on hledger.org.

    • Updated and committed hledger.org's caddy config and short urls (redirects)

    • Enabled https for code.hledger.org and site.hledger.org short urls.

    • Updated the stars.hledger.org redirect (we have reached the top 30 github-starred Haskell projects 🌟 🎉).

    • Set up a self-hosted Sandstorm server, and a public hledger-web instance (sandbox.hledger.org) in it that is fully writable (until spammers find it). Use it as a pastebin for examples, eg.

  • Github CI (continuous integration) workflows have been optimised somewhat:

    • Scheduled weekly builds have been disabled, as they were propagating to forks and running wastefully there in some cases.

    • Some repeated rebuilding of the hledger-lib and hledger packages that seems unnecessary has been stopped.

    • hledger-ui no longer builds its modules twice.

    • Haddock testing now done only at release time.

    • renamed main CI workflow and branch to "ci"

  • Tools:

    • .ghci: added an :rmain alias, which is like :main but reloads first - saves typing, and is useful eg when changing --debug level.

    • make haddock-watch is now fast

Finance

  • Updated project finance scripts, regenerated the journal with consistent precisions.

  • Updated reports with the last few months of data from Open Collective.

credits 1.32

Simon Michael, Jonathan Dowland, S. Zeid, Charlie Ambrose, Jacob Weisz, Peter Sagerson, Philipp Klocke, Stephen Morgan, bobobo1618.

2023-09-03 hledger-1.31

More tolerant equity/cost matching; print amounts in original style; multi-pivot.

hledger 1.31

Features

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

Improvements

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

Fixes

  • In journal files, valid multicommodity transactions where the matching non-equity postings can't be auto-detected are no longer considered an error (as they were in hledger 1.29 and 1.30). Now, such transactions are accepted, and --infer-cost has no effect on them. This is similar to the behaviour of --cost, --infer-equity, and --infer-market-prices. (#2045)

  • In journal files, equity conversion postings are now detected more tolerantly, using the same precision as the conversion posting's amount (#2041). Eg, the following transaction is now accepted:

    2023-01-01
        Assets               -84.01 USD @ 2.495 GEL
          ; ^ 209.60495 GEL, recognised as a match for the 209.60 below
        Equity:Conversion     84.01 USD
        Equity:Conversion   -209.60 GEL
        Assets               209.60 GEL
    
  • The roi command now reports TWR per period and overall TWR for multi-period reports. (#2068, Dmitry Astapov)

  • The commands list no longer shows bar when hledger-bar is not installed (#2065), and had a few other cleanups.

hledger-ui 1.31

Improvements

  • Allow megaparsec 9.5

hledger-web 1.31

Improvements

  • Allow aeson 2.2, megaparsec 9.5

project changes 1.31

Scripts/addons

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

  • A justfile implementation of ft and tt

Examples

  • self-tracking

  • RPG ledger (Eric Mertens)

Docs

Infrastructure

  • tools, CI: checkembeddedfiles, checkversions

  • Shake: avoid making empty commits

  • make functest-PAT: runs a subset of functional tests

  • Provide a ghc-tags.yaml file to make use of ghc-tags with Hledger easy.

    ghc-tags is a standalone tool to replace the formerly-built-in ":ctags" feature (and I presume ":etags") in GHCi. These walked over the source and produced a TAGS file (in vim-compatible ctags or Emacs-compatible etags format) that allows the relevant editors to quickly navigate around function definitions.

    ghc-tags trips over some of the CPP used in Hledger. The solution is to provide ghc-tags with explicit CPP defines via a YAML file. However, if a YAML file is provided, one also must specify the source paths, as the tool XORs config file | paths-on-command-line.

    See https://github.com/arybczak/ghc-tags/issues/6 for more information. (Jonathan Dowland)

credits 1.31

Simon Michael, Dmitry Astapov, Eric Mertens, Jay Neubrand, Jonathan Dowland.

2023-06-02 hledger-1.30.1

hledger 1.30.1

Fixes

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

Docs

  • Replace note about repeated options.

2023-06-01 hledger-1.30

Boolean queries, easier CSV file management, built-in demos, hledger-ui cash accounts screen, fixes.

hledger 1.30

Breaking changes

  • The CSV reader now properly skips all empty lines, as specified by docs. Previously, inner empty lines were not being skipped automatically. You might need to adjust the 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)

Features

  • CSV rules files can now be read directly, as in hledger -f foo.csv.rules CMD. By default this will read data from foo.csv in the same directory.

  • CSV rules files can use a new source 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)

Improvements

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

Fixes

  • Unbalanced virtual postings with no amount always infer a zero amount. This is fixing and clarifying the status quo; they always did this, but print always showed them with no amount, even with -x, and the behaviour was undocumented.

  • On windows systems with multiple drive letters, the commands list could fail to show all installed add-ons. (#2040)

  • Balancing a transaction with a balance assignment now properly respects costs. (#2039)

  • The commands list no longer lists non-installed addons. (#2034)

  • Since hledger 1.25, "every Nth day of month" period rules with N > 28 could be calculated wrongly by a couple of days when given certain forecast start dates. Eg ~ every 31st day of 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.

Docs

  • Miscellaneous manual cleanups.

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

  • Add Paging section.

  • Remove archaic mentions of setenv.

API

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

hledger-ui 1.30

Features

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

Improvements

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

Docs

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

  • Miscellaneous manual cleanups.

hledger-web 1.30

Fixes

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

Docs

  • Miscellaneous manual cleanups.

project changes 1.30

Scripts/addons

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

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

Examples

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

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

Docs

  • A shorter, more example-heavy home page on the website.

  • Simplified website and FAQ structure.

credits 1.30

Simon Michael, Chris Lemaire, Yehoshua Pesach Wallach.

2023-04-07 hledger-1.29.2

hledger 1.29.2

Breaking changes

  • 1.29's cleanup of the 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.

    (#2020)

Fixes

  • 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

Improvements

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

Fixes

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

hledger-web 1.29.2

Improvements

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

Fixes

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

project changes 1.29.2

Scripts/addons

  • hledger-install: re-enable hledger-interest, hledger-iadd; add hledger-lots

credits 1.29.2

Simon Michael

2023-03-16 hledger-1.29.1

hledger 1.29.1

Improvements

  • Hledger.Cli.Script now also exports

     Control.Applicative
     Control.Concurrent
     Data.Char
     Data.Functor
     System.IO
     System.IO.Error
    

    and new string helpers

     strip1Char
     stripBy
     strip1By
    
  • Allow building with GHC 9.6.1 (#2011)

Fixes

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

Docs

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

Features

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

Improvements

  • hledger's commands list has been reorganised for clarity. More add-on commands are now recognised and categorised, and unrecognised add-on commands are listed in a more compact multi-column layout. (Simon Michael, Michael Grünewald)

  • hledger's commands list and command line help now use ANSI (bold headings) when supported.

  • hledger's commands list and command line help now use a pager (respecting $PAGER) for long output except on MS Windows.

  • hledger's --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)

Fixes

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

  • Scripts can now use Hledger.Cli.Script, a convenient new prelude which helps reduce import boilerplate. It currently re-exports:

    Control.Monad
    Data.Either
    Data.List
    Data.Maybe
    Data.Ord
    Data.Time
    Text.Printf hiding (formatString)
    Data.Text (Text, pack, unpack)
    Safe hiding (at)
    System.Directory
    System.Environment
    System.Exit
    System.FilePath
    System.Process
    Hledger
    Hledger.Cli
    Hledger.Cli.Main (argsToCliOpts)
    

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

Docs

  • chunk the hledger manual into parts, rename and rearrange sections for better structure/flow
  • add a cheatsheet demonstrating all the main journal features that I recommend
  • move a number of my not-so-recommended journal features into a less visible "Other syntax" section
  • add: payees/descriptions completion
  • areg: more advice on account-matching
  • bal: --budget: clarify use of print --forecast
  • bal: budget: compare with forecasting; add some tips
  • balance cleanups/reorder
  • check: adjacentconversionpostings was dropped
  • cli: balance: fix link to Budgeting page
  • cli: fix all links to Journal > Tags / Commands > tags
  • codes: improve example suggested by Rob Nielsen
  • csv, timeclock, timedot: clarify comment lines (#1953)
  • csv: add new coinbase example
  • csv: clarify amount-in/amount-out docs (#1970)
  • csv: clarify skip/valid csv semantics (#1967)
  • csv: clarify valid CSV requirements and issues (fix #1966)
  • csv: cleanup, reorder, CSV rules tips -> Working with CSV
  • csv: fix wrong if tables doc; rewrite several sections (#1977)
  • csv: flatten, clean up CSV sections
  • csv: improve Amount field / Setting amounts
  • csv: note -in and -out are used together for one posting (#1970)
  • csv: rules factoring tips
  • csv: try to clarify how CSV fields and hledger fields work
  • document --infer-market-prices with signed costs (#1870)
  • fix duplicate market prices heading breaking info navigation
  • import: note a pitfall with multifile import
  • improve Directives summaries
  • introduction/input/output improvements
  • journal: cheatsheet: clarify date tag
  • journal: rewrite Account names, mention brackets/parentheses (#1915)
  • mention pivoting on a tag with multiple values (#1950)
  • more cost notation docs; describe Ledger and Beancount cost notation
  • more mention of posting order effect on inferring cost (#1959)
  • period expressions doc updates
  • Removed redundant paragraph in documentation. (J. B. Rainsberger)
  • rename directive sections, fix many links
  • reorganise commands list, like the CLI
  • reorganise bin/README & the Scripts page, add entries for recent scripts
  • replace "transaction prices" terminology with "costs"
  • tags: discuss multi-values/overriding (#1950)
  • update market price inference docs per sol
  • Updated section on pivoting. Used synonyms for "member" in cases where there could be confusion with the tag named "member." (Robert Nielsen)
  • use more standard and consistent boilerplate in hledger, ui, web man pages
  • virtual postings: improve wording per Robert Nielsen

hledger-ui 1.29

  • In the help dialog, mention that LEFT shows other screens.

  • In the manual, mention shift-up/down config needed for Terminal.app.

hledger-web 1.29

  • The add form's typeahead now shows non-ascii text correctly. (#1961) (Arsen Arsenović)

  • In the manual, improve --base-url's description. (#1562)

project changes 1.29

Scripts/addons

  • hledger-script-example.hs: rename/cleanup
  • sortandmergepostings: new, sorts postings and merges duplicates (Caleb Maclennan, Murukesh Mohanan)
  • hledger-register-max: new, prints the posting with largest historical balance
  • hledger-git: record shows better error output, no longer force-adds ignored files
  • hledger-git: status is fixed, also shows diffs
  • hledger-git: add short command aliases r, s, l
  • hledger-git: -h is fixed
  • hledger-git: pass unrecognised commands to git
  • hledger-install: also install hledger-edit, hledger-plot
  • hledger-install: add support for installing python packages
  • hledger-install: show quieter stack/cabal output
  • hledger-install: align install status list
  • hledger-install: don't list hledger-install.sh in PATH
  • hledger-install: drop hledger-iadd for now https://github.com/hpdeifel/hledger-iadd/issues/71

Docs

  • move most dev docs to doc/
  • Scripting hledger: move plugin types table here
  • Scripts: add hledger-plot, hledger-edit, hledger-fifo (Yann Büchau, Simon Michael)
  • update lots mockups, move to Mockups page
  • split Contributor Guide into Contributor Quick Start, LINKS, ISSUES
  • add REPOS, FILES, DECISIONS
  • CREDITS: updates, link to github contributors list

Infrastructure

  • pr template: mention COMMITS page and prefix convention (#1997)
  • make ghc 9.4 and current stackage nightly the default for dev builds
  • require megaparsec 9.3+ in dev builds, for its useful dbg tool
  • make site-watch: fix runaway recursion, be more verbose
  • new make rules: man-watch
  • new tools: ciwatch, push, pushdocs, gtree
  • misc process updates

credits 1.29

Simon Michael, Chris Lemaire, Caleb Maclennan, Jonathan Dowland, J. B. Rainsberger, Michael Grünewald, Robert Nielsen, Yann Büchau.

2022-12-01 hledger-1.28

new hledger-ui screens, better debug output; accounts, print, csv-reading improvements; new hledger-move, watchaccounts scripts

hledger 1.28

Features

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

    (#1936)

Improvements

  • print --match now respects -o and -O.

  • print --match now returns a non-zero exit code when there is no acceptable match.

  • Support megaparsec 9.3. (Felix Yan)

  • Support GHC 9.4.

Fixes

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

Scripts/addons

  • bin/hledger-move helps record transfers involving subaccounts and costs, eg when withdrawing some or all of an investment balance containing many lots and costs.

  • bin/hledger-git no longer uses the non-existent git record command. (#1942) (Patrick Fiaux)

  • bin/watchaccounts is a small shell script for watching the account tree as you make changes.

hledger-ui 1.28

Features

  • New "Balance sheet accounts" and "Income statement accounts" screens have been added, along with a new top-level "Menu" screen for navigating between these and the "All accounts" screen.

  • hledger-ui now starts in the "Balance sheet accounts" screen by default (unless no asset/liability/equity accounts can be detected, or command line account query arguments are provided). This provides a more useful default view than the giant "All accounts" list. Or, you can force a particular starting screen with the new --menu/--all/--bs/--is flags (eg, hledger-ui --all to replicate the old behaviour).

Improvements

  • The ENTER key is equivalent to RIGHT for navigation.

  • hledger-ui debug output is now always logged to ./hledger-ui.log rather than the console, --debug with no argument is equivalent to --debug=1, and debug output is much more informative.

  • Support GHC 9.4.

  • Support megaparsec 9.3 (Felix Yan)

  • Support (and require) brick 1.5, fsnotify 0.4.x.

Fixes

  • Mouse-clicking in empty space below the last list item no longer navigates back. It was too obtrusive, eg when you just want to focus the window. You can still navigate back with the mouse by clicking the left edge of the window.

  • A possible bug with detecting change of date while in --watch mode has been fixed.

API

  • hledger-ui's internal types have been changed to allow fewer invalid states
    and make it easier to develop and debug. (#1889, #1919).

  • Debug logging helpers have been added and cleaned up in Hledger.Ui.UIUtils: dbgui dbguiIO dbguiEv dbguiScreensEv mapScreens screenId screenRegisterDescriptions

hledger-web 1.28

Improvements

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

  • Allow megaparsec 9.3 (Felix Yan)

  • Support GHC 9.4

project changes 1.28

Docs

  • Miscellaneous improvements.

Examples

  • Indian National Pension Service CSV rules (Pranesh Prakash)

Infrastructure

  • make site-watch: switch from entr to watchexec.

  • make hoogle-setup, hoogle-serve: run a local hoogle on hledger code.

  • make man-watch-PROG: watch a hledger program's man page as source files change.

credits 1.28

Simon Michael, Felix Yan, Patrick Fiaux.

2022-09-18 hledger-1.27.1

hledger 1.27.1

Fixes

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

Fixes

  • The add form no longer gives an error when there is just a single file and no file field showing. (#1932)

  • Uses hledger-1.27.1

2022-09-01 hledger-1.27

Infer costs from equity postings, new error checks, improved error messages, fixes.

hledger 1.27

Features

  • hledger check recentassertions (and flycheck-hledger in Emacs if you enable this check) requires that all balance-asserted accounts have a balance assertion within 7 days before their latest posting.

    This helps remind you to not only record transactions, but also to regularly check account balances against the real world, to catch errors sooner and avoid a time-consuming hunt.

  • The --infer-costs general flag has been added, as the inverse operation to --infer-equity. --infer-costs detects commodity conversion transactions which have been written with equity conversion postings (the traditional accounting notation) and adds PTA cost notation (@@) to them (allowing cost reporting). See https://hledger.org/hledger.html#equity-conversion-postings . (Stephen Morgan)

Improvements

  • Many error messages have been improved. Most error messages now use a consistent, more informative format. (#1436)

  • The accounts command has a new --directives flag which makes it show valid account directives which you can paste into a journal.

  • The accounts command has a new --positions flag which shows where accounts were declared, useful for troubleshooting. (#1909)

  • Bump lower bounds for Diff and githash. (Andrew Lelechenko)

  • GHC 8.6 and 8.8 are no longer supported. Building hledger now requires GHC 8.10 or greater.

Fixes

  • Account display order is now calculated correctly even when accounts are declared in multiple files. (#1909)

  • At --debug 5 and up, account declarations info is logged. (#1909)

  • hledger aregister and hledger-ui now show transactions correctly when there is a type: query. (#1905)

  • bal: Allow cumulative gain and valuechange reports. Previously, --cumulative with --gain or --valuechange would produce an empty report. This fixes this issue to produce a reasonable report. (Stephen Morgan)

  • bal: budget goal amounts now respect -c styles (fixes #1907)

  • bal: budget goals now respect -H (#1879)

  • bal: budget goals were ignoring rule-specified start date

  • cf/bs/is: Fixed non-display of child accounts when there is an intervening account of another type. (#1921) (Stephen Morgan)

  • roi: make sure empty cashflows are skipped when determining first cashflow (Charlotte Van Petegem) Empty cashflows are added when the begin date of the report is before the first transaction.

Scripts/addons

  • https://hledger.org/scripts.html - an overview of scripts and addons in bin/.

  • paypaljson, paypaljson2csv - download txns from paypal API

  • hledger-check-postable.hs - check that no postings are made to accounts with a postable:(n|no) tag

  • hledger-addon-example.hs - script template

hledger-ui 1.27

Improvements

  • At --debug=2 and up, log debug output to ./debug.log.

  • Use/require brick 1.0+. (#1889)

  • Use hledger 1.27

hledger-web 1.27

Improvements

  • Improve the add form's layout and space usage.

  • Pre-fill the add form's date field.

  • Highlight today in the add form's date picker.

  • Focus the add form's description field by default.

  • Allow an empty description in the add form.

  • Use hledger 1.27

Fixes

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

project changes 1.27

Docs

  • https://hledger.org/ERRORS.html - an overview of hledger's error messages.

  • Rewrite/consolidate cost and conversion docs.

  • New template for github releases, with improved install instructions for binaries.

  • Add modern windows binary install instructions. (Lazar Lazarov, Simon Michael)

  • Fix tables of contents in developer documentation. (Alex Hirzel)

  • Update ACHIEVEMENTS. (Alex Hirzel)

  • Corrected the extension for the CREDITS file. (Pranesh Prakash)

  • Fix broken link in bin/README.md. (David D Lowe)

Examples

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

Process

  • Revive github projects, set up http://projects.hledger.org shortcut url

  • Many cleanups and improvements to the CI test and binary-generating github actions. The CI tests for master now also include hledger-lib's doctests.

  • All packages now disallow name shadowing in their code.

  • make scc gives a modern report of code line counts.

  • make ghci-unit-test loads hledger-lib unit tests in GHCI.

credits 1.27

Simon Michael, Stephen Morgan, Alex Hirzel, Pranesh Prakash, David D Lowe, Charlotte Van Petegem, Max Thomas, Andrew Lelechenko.

2022-07-11 hledger-1.26.1

hledger 1.26.1

  • require safe 0.3.19+ to avoid deprecation warning

hledger-ui 1.26.1

  • support doclayout 0.4, brick 0.72+

  • require safe 0.3.19+ to avoid deprecation warning

2022-06-04 hledger-1.26

Miscellaneous improvements.

hledger 1.26

Improvements

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

Fixes

  • 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

Fixes

  • Don't add link URLs when printing.

Improvements

  • Now builds with GHC 9.2.

  • Uses hledger 1.26.

project changes 1.26

Scripts/addons

  • renamed hledger-number.sh to hledger-simplebal

  • added hledger-git, hledger-pijul

  • fin (and bin) scripts show available scripts and their help

  • renamed aliases.sh to bashrc

  • Get hledger-print-location working. (Stephen Morgan)

Docs

  • README cleanup, inspired by feedback from README reviewer Lars Wirzenius.

  • Clearer sponsoring info and more complete sponsor lists on website and README.

  • The new https://github.com/simonmichael/hledger_finance repo keeps track of our public finances (on Open Collective, Liberapay etc.)

Examples

  • invoice: calculate dates accurately on last days of month

Process

  • Stackage nightly and GHC 9.2 are now the default for dev builds.

  • CI workflows:

    • Workflows and binaries have more consistent naming, mentioning platform and architecture.
    • The main test workflow is now linux-x64-test, 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.

Features

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

    2000-01-01
      a             1 AAA @@ 2 BBB
      b            -2 BBB
    
    $ hledger print --infer-equity
    2000-01-01
      a                               1 AAA
      equity:conversion:AAA-BBB:AAA  -1 AAA
      equity:conversion:AAA-BBB:BBB   2 BBB
      b                              -2 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)

Improvements

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

Fixes

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

Documentation

hledger-ui 1.25

  • Uses hledger 1.25.

hledger-web 1.25

  • Uses hledger 1.25.

project changes 1.25

Scripts/addons

  • hledger-install.sh now also installs Pavan Rikhi's hledger-stockquotes tool.

  • The bin/hledger-number addon was added.

  • The bin/hledger-check-fancyassertions addon now shows docs in --help.

  • A new invoice-making script was added: examples/invoicing/invoice-script/invoice

Process/tools

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

Fixes

  • 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

Fixes

  • An extra "root" account is no longer shown (a regression in 1.24). (#1782)

  • Declared accounts are now filtered correctly by a not:ACCT query. (#1783)

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

hledger-web 1.24.1

Fixes

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

2021-12-01 hledger-1.24

New report layout options with less eliding, hledger-ui mouse support, misc fixes and improvements.

hledger 1.24

Features

  • balance commands provide more control over how multicommodity amounts are displayed. (And they no longer elide too-wide amounts by default.) The --commodity-column flag has been deprecated and replaced by a new --layout option, with three values:

    • wide (the default, shows amounts on one line unelided, like older hledger versions)
    • tall (a new display mode, shows one amount per line)
    • bare (like the old --commodity-columm, shows one commodity per line with symbols in their own column)

    (Stephen Morgan)

  • The balance commands have a new --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)

Improvements

  • The stats command now shows rough but useful performance stats: run time and processing speed in transactions per second.

  • balance: support the --related flag, like register, showing the other postings from the transactions. (#1469, Stephen Morgan)

  • roi now uses posting dates when available, and honors the --date2 flag. This will not change the results computed for the typical use-case, it just makes "roi" more thorough/consistent. (Dmitry Astapov)

  • aregister now shows transactions' secondary date if the --date2 flag is used. (#1731)

  • timedot: a D default commodity (and style) declared in a parent journal file will now be applied to timedot amounts. This means they can be priced and valued/converted.

  • cli: The --pretty and --forecast options can now be written after the command name, like other general options. (Stephen Morgan)

  • register -V -H with no interval now values at report end date, like balance. (#1718, Stephen Morgan)

  • Allow megaparsec 9.2.

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

Fixes

  • prices: Do not include zero amounts when calculating amounts for balance assignments. This is not usually a problem, but can get in the way of auto-inferring prices. (#1736, Stephen Morgan)

  • csv: Successfully parse an empty csv file. (#1183, Stephen Morgan)

  • balance: Balance reports with --depth=0 properly report aggregated values, not zero everywhere. (#1761, Stephen Morgan)

  • prices: Do not try to generate prices when there would be a zero denominator. Also correctly generate reverse prices for zero amounts. (Stephen Morgan)

  • csv: Allow both amount-in and amount-out fields to contain a zero. (#1733, Stephen Morgan)

  • balance: Balance reports should consider date: queries when calculating report span with --date2. (#1745, Stephen Morgan)

  • print: auto: The print command should always display inferred amounts for --auto generated postings. (#1276, Stephen Morgan)

hledger-ui 1.24

Features

  • hledger-ui can now be controlled with mouse or touchpad. Click to enter things, click left margin or bottom blank area to return to previous screen, and use mouse wheel / swipe to scroll.

  • In addition to accounts with postings, hledger-ui now also shows declared accounts, even if they are empty (just leaf accounts, not parents). The idea is to show a useful list of accounts out of the box, when all you have is a starter file with account declarations.

Improvements

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

Fixes

  • When an invalid regular expression is entered at the / (filter) prompt, we now display an error instead of silently ignoring it. (#1394, Stephen Morgan)

  • Entering the register screen now always positions the selection mid-screen. Previously it would be at bottom of screen on the first entry.

  • Report layout in the terminal is now robust with more kinds of wide characters, such as emoji. (#895, Stephen Morgan)

hledger-web 1.24

Improvements

  • Allow megaparsec 9.2

project changes 1.24

Software

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

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

Docs

  • Account types: prioritise the short one-letter names, hide the deprecated legacy syntax.

  • Directives: a more compact and accurate overview.

  • examples/templates/basic: A new starter file set, and a place to collect them.

  • Expose more developer docs as separate web pages: CHANGELOGS, COMMITS, RELEASING, etc.

  • Fix a link to developer workflows. (Joaquin "Florius" Azcarate)

Process

  • PR template: Fix our github PR template to use proper comment syntax, and link to more relevant docs. (toonn)

  • cabal.project: Drop obsolete compatibility comment. (#1365, toonn)

  • Bump default stackage snapshot to one avoiding buggy happy version.

  • bin/changelog: a new helper making changelog edits more pleasant.

  • make throughput{,-dev,-EXE}: reports transactions per second for a range of file sizes with the hledger in PATH, hledger dev build, or named hledger executable.

  • make install-as-FOO: build executables and save as bin/hledger*-FOO

  • perf: bench-ledger.sh for comparative benchmarking with Ledger.

  • CI: commitlint: be more forgiving when we can't figure out recent commits (don't check any).

  • CI: commitlint: recognise any commit starting with ‘Merge’ as a merge commit (and ignore it). (Stephen Morgan)

credits 1.24

Simon Michael, Stephen Morgan, toonn, Pranesh Prakash, Dmitry Astapov, ShrykeWindgrace, Joaquin Azcarate, Lawrence Wu.

2021-09-21 hledger-1.23

Capital gains report, separate symbol/number display, command line commodity styling, budget selection, weekday/weekend recurrence, 10% speedup, fixes.

project changes 1.23

Software:

  • The bin/hledger-check-fancyassertions.hs addon script, allowing more complex balance assertions, works again. (#1464, Stephen Morgan)

  • Many code cleanups suggested by hlint (Stephen Morgan)

Docs:

  • Added a public BACKLOG.org to the hledger repo and website.

  • Website updates:

    • Reorganised site content.
    • Improved page tables of contents.
    • Content fixes.
    • New docs: Currency conversion. hledger and Beancount/GnuCash/Ledger/Quicken.
  • New examples: systemd and nginx configs for hledger-web (Alan Young)

Tools/process:

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

Features

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

Improvements

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

Fixes

  • A rare bug causing incorrect balances to be reported by the cf/bs/bse/is commands, since hledger 1.19, has been fixed. (cf/bs/bse/is with --tree --no-elide --begin DATE and certain account directives could show wrong balances). (#1698, Stephen Morgan)

  • aregister now aligns multicommodity amounts properly (broken since 1.21). (#1656, Stephen Morgan)

  • balance -E (and hledger-ui Z) now correctly show zero parent accounts, fixing a bug introduced in hledger 1.19. (#1688, Stephen Morgan)

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

Improvements

  • Depend on hledger 1.23.

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

hledger-web 1.23

Improvements

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

  • Depend on hledger 1.23.

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

Fixes

  • Toggle showing zero items properly even when called with --empty. (#1237, Stephen Morgan)

  • Do not hide empty accounts if they have non-empty subaccounts. (#1237, Stephen Morgan)

  • Allow unbalanced postings (parenthesised account name) in the add transaction form. (#1058, Stephen Morgan)

  • An XSS (cross-site scripting) vulnerability has been fixed. Previously (since hledger-web 0.24), javascript code could be added to any autocompleteable field and could be executed automatically by subsequent visitors viewing the journal. Thanks to Gaspard Baye and Hamidullah Muslih for reporting this vulnerability. (#1525, Arsen Arsenović)

credits 1.23

Simon Michael, Stephen Morgan, Lawrence Wu, Jakob Schöttl, Dmitry Astapov, Malte Brandy, Arsen Arsenović, Arjen Langebaerd, Alan Young, Daniel Gröber.

2021-08-07 hledger-1.22.2

hledger 1.22.2

Breaking changes

  • aregister no longer hides future transactions by default. This is a consequence of the fix for #1638. It makes aregister consistent, so we think it's a reasonable change. So if you have future-dated transactions in your journal which you don't want reported, you now must exclude them with -e tomorrow or date:-tomorrow in the command, as with other reports. (Stephen Morgan)

Improvements

  • Timedot format's doc has been rewritten.

Fixes

  • Make balance assignments in forecasted transactions work again (broken in 1.22.1). Forecast transactions are now generated early and processed in the same way as other transactions. (#1638, Stephen Morgan)

  • aregister preserves the order of same-day transactions again (broken in 1.22.1). (#1642, Stephen Morgan)

hledger-ui 1.22.2

  • Use hledger 1.22.2.

hledger-web 1.22.2

  • Use hledger 1.22.2.

credits 1.22.2

Simon Michael, Stephen Morgan.

2021-08-02 hledger-1.22.1

hledger 1.22.1

Improvements

  • Bash shell completions (for hledger, hledger-ui, hledger-web) are now included in the hledger package's release tarballs, making them more likely to be installed by system packages. (Jakob Schöttl)

  • roi docs now discuss how to quote multi-word queries. (#1609, Dmitry Astapov)

  • Allow megaparsec 9.1

Fixes

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

Improvements

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

  • Allow megaparsec 9.1.

Fixes

  • Up/down keys work on the transaction screen again (broken since 1.22). (#1607, Stephen Morgan)

  • Fix a possible off-by-one bug with valuation date when using 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

Improvements

  • deps: Allow megaparsec 9.1.

Fixes

  • The register chart works again when there are multiple commodities and transaction prices (broken since 1.22). (#1597, Stephen Morgan)

credits 1.22.1

Simon Michael, Stephen Morgan, Jakob Schöttl, Dmitry Astapov.

2021-07-03 hledger-1.22

Optimisations, bugfixes. (announcement)

project changes 1.22

Software:

  • We now provide static executables for GNU/Linux on x64 (amd64) and arm32v7 architectures. These are more portable and more likely to work on your linux system than the dynamic Ubuntu executables we have been providing. These will also be useful for Nextcloud.com users. (#1571) (Garret McGraw)

  • GHC 9.0 support has been added. We have dropped official support for GHC 8.0/8.2/8.4; building hledger now requires GHC 8.6 or newer.

Docs:

  • The info manuals now have the proper metadata so you or your packager can install them with install-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)

Chat:

  • The hledger IRC channels ( #hledger:libera.chat, #hledger-bots:libera.chat ) moved to Libera.chat.

  • The hledger Matrix room ( #hledger:matrix.org ), is now on at least equal "official" footing with the IRC channel.

  • I upgraded the matrix room to a newer version of the Matrix protocol. This effectively splits it into an old (read only) room and a new room. If you are joined to the old room, you might not have noticed; in your matrix client, please follow the link to the new room, ie #hledger:matrix.org.

  • I briefly bridged the IRC and matrix rooms, because having two chats (four if we consider #plaintextaccounting) is a pain. I hope to try the experiment again at some point.

hledger 1.22

Features

  • check: A new 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)

Improvements

  • Many internal optimisations have been applied (cf hledger-lib changelog). Overall, you can expect most reports to be about 20% faster. The register report is more than 2x faster and uses 4x less memory. (Stephen Morgan)

    ~/src/hledger$ quickbench -w hledger-1.21,hledger
    Running 5 tests 1 times with 2 executables at 2021-06-29 13:13:26 HST:
    
    Best times:
    +----------------------------------------------------++--------------+---------+
    |                                                    || hledger-1.21 | hledger |
    +====================================================++==============+=========+
    | -f examples/10000x1000x10.journal print            ||         1.18 |    0.90 |
    | -f examples/10000x1000x10.journal register         ||        12.82 |    5.95 |
    | -f examples/10000x1000x10.journal balance          ||         1.38 |    0.86 |
    | -f examples/1000x1000x10.journal balance --weekly  ||         0.96 |    0.78 |
    | -f examples/10000x1000x10.journal balance --weekly ||        13.07 |   10.79 |
    +----------------------------------------------------++--------------+---------+
    
  • ANSI color is now disabled automatically (on stdout) when the -o/--output-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)

Fixes

  • 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

Improvements

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

Fixes

  • 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

Improvements

  • The --version flag shows more detail (git tag/patchlevel/commit hash, platform/architecture). (Stephen Morgan)

  • Allow yesod-form 1.7 (Felix Yan)

  • Add now-required lower bound on containers. (#1514)

  • GHC 9.0 is now officially supported. GHC 8.0, 8.2, 8.4 are no longer supported; we now require GHC 8.6 or greater.

Fixes

  • In the add form, fix a bug where extra posting rows were not added when needed in certain web browsers. (charukiewicz)

credits 1.22

This release was brought to you by Simon Michael, Stephen Morgan, Felix Yan, crocket, Eric Mertens, Damien Cassou, charukiewicz, and Garret McGraw.

2021-03-10 hledger-1.21

More speed; more cli-accessible docs; value change report; improvements to balance reports, valuation and more (announcement)

project-wide changes 1.21

  • roi has a new cookbook doc, and example files have been updated. (Dmitry Astapov)

  • Example CSV rules for the Daedalus wallet have been added.

  • The default stackage resolver/GHC version has been bumped to lts-17.4/ghc-8.10.4.

  • tools/generatejournal now includes more commodities and prices in generated journals. (Stephen Morgan)

  • Our functional tests now also run on BSD. (#1434, Felix Van der Jeugt)

  • Addon scripts in bin/ have been updated for latest hledger API (Stephen Morgan).

  • Addon scripts are now compiled as part of our CI tests, and always with the same version of hledger source they were shipped with. We now require script users to check out the hledger source tree and run the scripts (or, bin/compile.sh) from there. This keeps users and tests in sync, making things more reliable for everyone. (#1453)

  • Last but not least, hledger's bash completions (provided in ./shell-completions/) have been thoroughly updated (#1404, #1410, Vladimir Zhelezov).

hledger cli 1.21

general

  • hledger is now generally about 10% more memory- and time-efficient, and significantly more so in certain cases, eg journals with many total transaction prices. (Stephen Morgan)

  • The --help/-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.

valuation

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

commands

  • add: Infix matches are now scored higher. If the search pattern occurs in full within the other description, that match gets a +0.5 score boost.

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

general

  • strict mode: with -s/--strict, hledger requires that all accounts and commodities are declared with directives.

  • Reverted a stripAnsi change in 1.19.1 that caused a 3x slowdown of amount rendering in terminal reports. (#1350)

  • Amount and table rendering has been improved, so that stripAnsi is no longer needed. This speeds up amount rendering in the terminal, speeding up some reports by 10% or more since 1.19. (Stephen Morgan)

  • Amount eliding no longer displays corrupted ANSI codes (#1352, Stephen Morgan)

  • Eliding of multicommodity amounts now makes better use of available space, avoiding unnecessary eliding (showing as many amounts as possible within 32 characters). (Stephen Morgan)

  • Command line help for --no-elide now mentions that it also disables eliding of multicommodity amounts.

  • Query terms containing quotes (eg to match account names containing quotes) now work properly. (#1368, Stephen Morgan)

  • cli, journal: Date range parsing is more robust, fixing failing/incorrect cases such as: (Stephen Morgan)

    • a hyphenated range with just years (2017-2018)
    • a hyphenated date with no day in a hyphenated range (2017-07-2018)
    • a dotted date with no day in a dotted range (2017.07..2018.02)
  • Debug output is prettier (eg, in colour), using pretty-simple instead of pretty-show.

  • csv, timedot, timeclock files now respect command line --alias options, like journal files. (#859)

  • Market price lookup for value reports is now more robust, fixing several bugs (and debug output is more informative). There has been a slight change in functionality: when chaining prices, we now prefer chains of all "forward" prices, even if longer, with chains involving reverse prices being the last resort. (#1402)

commands

  • add: number style (eg thousands separators) no longer disturbs the value that is offered as default. (#1378)

  • bal: --invert now affects -S/--sort-amount, reversing the order. (#1283, #1379) (Stephen Morgan)

  • bal: --budget reports no longer insert an extra space inside the brackets. (Stephen Morgan)

  • bal: --budget reports now support CSV output (#1155)

  • bal, is, bs --change: Valued multiperiod balance change reports now show changes of value, rather than the value of changes. (#1353, Stephen Morgan)

  • bal: clearer debug output, following debug levels policy

  • check: A new command which consolidating the various check-* commands. It runs the default, strict, or specified checks and produces no output and a zero exit code if all is well.

  • check-dates: this command is deprecated and will be removed in next release; use "hledger check ordereddates" instead.

  • check-dupes: this command is deprecated and will be removed in next release; use "hledger check uniqueleafnames" instead.

  • import: The journal's commodity styles (declared or inferred) are now applied to imported amounts, overriding their original number format.

  • roi: TWR now handles same-day pnl changes and cashflows, calculation failure messages have been improved, and the documentation includes more detail and examples. (#1398) (Dmitry Astapov)

journal format

  • The journal's commodity styles are now applied to forecasted transactions. (#1371)

  • journal, csv: commodity style is now inferred from the first amount, as documented, not the last. This was "working wrongly" since hledger 1.12..

  • A zero market price no longer causes "Ratio has zero denominator" error in valued reports. (#1373)

csv format

  • The new decimal-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

new

  • aregister: a new command showing a transaction-oriented register for a single account. This is like hledger-ui, hledger-web, or your bank statement, and unlike the register command which shows individual postings possibly spanning multiple accounts. You might prefer aregister when reconciling real-world asset/liability accounts, and register when reviewing detailed revenues/expenses. (#1294)

  • codes: a new command for listing transaction codes

  • print: a new 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)

improved

  • Reports involving multiple commodities now show at most two commodities per amount by default, making multicolumn reports less wide and more readable. Use the --no-elide flag to prevent this.

  • Flat (AKA list) mode is now the consistent default used by all balance reports and other commands showing accounts. (Stephen Morgan)

  • All commands supporting tree/list mode now accept -t and -l as short forms of the --tree and --flat flags. (#1286)

  • account,bal,bs,cf,is: --drop now also works in tree mode (Stephen Morgan)

  • bal,bs,cf,is: tabular balance reports now elide (compress) boring parent accounts, like the non-tabular reports. (Stephen Morgan)

  • bal,bs,cf,is: monthly column headings are no longer be displayed as just the month abbreviations, if multiple years are being displayed.

  • bal --budget: with --cumulative or --historical, column headings now correctly show the period end dates rather than date spans.

  • bs,cf,is: --no-total now hides subtotals as well as the grand total (Stephen Morgan)

  • bs,cf,is: -%/--percent no longer implies --no-total. (Stephen Morgan)

  • roi: errors are now shown without a call stack

  • tags: the new --parsed flag causes all tags or values to be shown, including duplicates, in the order they were parsed. Blank/empty values are omitted by default and can be shown with -E/--empty.

  • Debug output is now organised better by debug level. The levels are:

    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

correctness/robustness

  • Added a missing lower bound for aeson, making cabal installs more reliable. (#1268)

  • When parsing dates, we now require the year to have at least four digits. So eg Feb 1 in the year 10 would need to be written 0010-02-01, 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

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

commands

  • add: you can use < to undo and redo previous inputs (Gaith Hallak)

  • bs, cf, is, bal, print, reg: support json output

  • bs, cf, is: fix excess subreport columns in csv output

  • bs, cf, is, bal: fix an issue with border intersections in --pretty-tables output. (Eric Mertens)

  • close: fix a rounding bug that could generate unbalanced transactions. (#1164)

  • close: hide cost prices by default, show them with --show-costs. close no longer preserves costs (transaction prices) unless you ask it to, since that can generate huge entries when there are many foreign currency/investment transactions. (#1165)

  • close: equity amounts are omitted by default, for simpler entries; -x/--explicit shows them (usually causing more postings). (#1165)

  • close: --interleaved generates equity postings alongside each closed account, making troubleshooting easier.

  • close: "equity:opening/closing balances" is now the default closing and opening account.

  • close: --close-desc/--open-desc customise the closing/opening transaction descriptions. (#1165)

  • close: some --open*/--close* flags have been simplified for memorability:

    --closing -> --close
    --opening -> --open
    --close-to -> --close-acct
    --open-from -> --open-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.

commands

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

commands

  • add, web: on Windows, trying to add transactions to a file path containing trailing periods (eg hledger add -f Documents.\.hledger.journal) now gives an error, since this could cause data loss otherwise (#1056). This affects the add command and hledger-web's add form.

  • bal: --budget: don't always convert to cost.

  • bal: --budget: don't show a percentage when budgeted and actual amounts are in different commodities.

  • bal/bs/bse: -H/--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:

    /accountnames
    /transactions
    /prices
    /commodities
    /accounts
    /accounttransactions/ACCT
    

    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 http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @in.json; echo
    

    (#316)

  • fix unbalanced transaction prevention in the add form

  • fix transaction-showing tooltips (#927)

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

Developers:

  • developer docs have moved from the wiki into CONTRIBUTING.md (#920)

  • new streamlined changelog update process. Shake targets:

    ./Shake changelogs
    ./Shake CHANGES.md
    ./Shake CHANGES.md-dry
    ./Shake PKG/CHANGES.md
    ./Shake PKG/CHANGES.md-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

    changed:

    • amultiplier -> aismultiplier

    • Amount fields reordered for clearer debug output

    • tpreceding_comment_lines -> tprecedingcomment, reordered

    • Hledger.Data.TransactionModifier.transactionModifierToFunction -> modifyTransactions

    • Hledger.Read.Common.applyTransactionModifiers -> Hledger.Data.Journal.journalModifyTransactions

    • HelpTemplate -> CommandDoc

credits 1.13

Release contributors: Simon Michael, Jakob Schöttl, Dmitry Astapov.

2018/12/02 hledger 1.12

Account type declarations, complete balance assertions, GHC 8.6 support, hledger-ui usability updates, misc fixes (mail)

hledger cli 1.12

  • install script: ensure a new-enough version of stack; more informative output

  • build with GHC 8.6/base-4.12 (Peter Simons)

  • add required upper bound for statistics (Samuel May)

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

    1/1
      (a)  A 1
      (a)  B 1
      (a)  0   =  A 1   ; commodity A balance assertion, succeeds
      (a)  0   == A 1   ; complete balance assertion, fails
    
  • journal format: account directives now allow whitespace or a comment after the account name

  • journal format: using ~ for home directory in include directives now works (#896) (Mykola Orliuk)

  • journal format: prevent misleading parse error messages with cyclic include directives (#853) (Alex Chen)

  • journal format: transaction modifier multipliers handle total-priced amounts correctly (#928).
    Multipliers (*N) in transaction modifier rules did not multiply total-priced amounts properly. Now the total prices are also multiplied, keeping the transaction balanced.

  • journal format: do amount inference/balance assignments/assertions before transaction modifiers (#893, #908) (Jesse Rosenthal)
    Previously, transaction modifier (auto postings) rules were applied before missing amounts were inferred. This meant amount multipliers could generate too many missing-amount postings, making the transaction unbalanceable (#893).

    Now, missing amount inference (and balance assignments, and balance assertions, which are interdependent) are done earlier, before transaction modifier rules are applied (#900, #903).

    Also, we now disallow the combination of balance assignments and transaction modifier rules which both affect the same account, which could otherwise cause confusing balance assertion failures (#912). (Because assignments now generate amounts to satisfy balance assertions before transaction modifier rules are applied (#908).)

  • journal format: periodic transaction rules are now aware of Y default year directives. (#892)
    Ie when a default year Y is in effect, they resolve partial or relative dates using Y/1/1 as the reference date, rather than today's date.

hledger-ui 1.12

  • fix "Any" build error with GHC < 8.4

  • error screen: always show error position properly (#904) (Mykola Orliuk)

  • accounts screen: show correct balances when there's only periodic transactions

  • drop the --status-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 0.3.0.1+

  • uses hledger-lib 1.10

hledger-web 1.10

  • view, add, edit permissions can be set at CLI or by Sandstorm HTTP header

  • the edit form has been revived, for whole-journal editing

  • the journal can now be uploaded and downloaded

  • the e key toggles empty accounts in the sidebar

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

(mail)

Release contributors: Simon Michael, Dmitry Astapov, Mykola Orliuk, Eli Flanagan, Elijah Caine, Sam Jeeves, Matthias Kauer, Hans-Peter Deifel, Mick Dekkers, Nadrieril, Alvaro Fernando García.

project-wide changes 1.5

  • remove upper bounds on all but hledger* and base (experimental) It's rare that my deps break their api or that newer versions must be avoided, and very common that they release new versions which I must tediously and promptly test and release hackage revisions for or risk falling out of stackage. Trying it this way for a bit.

hledger-lib 1.5

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

Tools

make ghci-prof starts GHCI in profiling mode, enabling stack traces with traceStack

make ghci-web now also creates required symlinks

make site-reload opens an auto-reloading browser on the latest site html

make changelog-draft shows the commits since last tag as org nodes

hledger-lib 1.3

journal format

The "uncleared" transaction/posting status (and associated UI flags and keys) has been renamed to "unmarked" to remove ambiguity and confusion. See the issue and linked mail list discussion for more background. (#564)

csv format

In CSV conversion rules, assigning to the "balance" field name creates balance assertions (#537, Dmitry Astapov).

Doubled minus signs are handled more robustly (fixes #524, Nicolas Wavrant, Simon Michael)

Misc

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

Deps: allow megaparsec 5.3.

hledger cli 1.3

CLI

The "uncleared" transaction/posting status, and associated UI flags and keys, have been renamed to "unmarked" to remove ambiguity and confusion. This means that we have dropped the --uncleared flag, and our -U flag now matches only unmarked things and not pending ones. See the issue and linked mail list discussion for more background. (#564)

Also the -P short flag has been added for --pending, and the -U/-P/-C flags can be combined.

bs/is: fix "Ratio has zero denominator" error (#535)

bs/is/cf: fix --flat (#552) (Justin Le, Simon Michael)

bal/bs/is/cf: show negative amounts in red (Simon Michael, Justin Le). These commands now show negative amounts in red, when hledger detects that ANSI codes are supported, (ie when TERM is not "dumb" and stdout is not being redirected or piped).

print: show pending mark on postings (fixes #563). A pending mark on postings is now displayed, just like a cleared mark. Also there will now be a space between the mark and account name.

print: amounts are now better aligned, eg when there are posting status marks or virtual postings.

Addons

prices: add --inverted-costs flag, sort output, increase precision (Mykola Orliuk)

rewrite: add support for rewriting multipler postings into different commodities. For example, postings in hours can be used to generate postings in USD. (#557) (Christian G. Warden)

make addons compiles the experimental add-ons.

hledger-ui 1.3

The register screen now shows transaction status marks.

The "uncleared" status, and associated UI flags and keys, have been renamed to "unmarked" to remove ambiguity and confusion. This means that we have dropped the --uncleared flag, and our -U flag now matches only unmarked things and not pending ones. See the issue and linked mail list discussion for more background. (#564)

The P key toggles pending mode, consistent with U (unmarked) and C (cleared). There is also a temporary --status-toggles flag for testing other toggle styles; see hledger-ui -h. (#564)

There is now less "warping" of selection when lists change:

  • When the selected account disappears, eg when toggling zero accounts, the selection moves to the alphabetically preceding item, instead of the first one.

  • When the selected transaction disappears, eg when toggling status filters, the selection moves to the nearest transaction by date (and if several have the same date, by journal order), instead of the last one.

In the accounts and register screens, you can now scroll down further so that the last item need not always be shown at the bottom of the screen. And we now try to show the selected item centered in the following situations:

  • after moving to the end with Page down/End
  • after toggling filters/display modes (status, real, historical..)
  • on pressing the control-l key (this forces a screen redraw, also)
  • on entering the register screen from the accounts screen (except the first time, a known problem).

Items near the top won't be centered because we don't scroll above the top of the list.

Emacs movement keys are now supported, as well as VI keys. CTRL-b/CTRL-f/CTRL-n/CTRL-p and hjkl should work wherever unmodified arrow keys work.

In the transaction screen, amounts are now better aligned, eg when there are posting status marks or virtual postings.

Deps: allow brick 0.19 (#575, Felix Yan, Simon Michael)

hledger-web 1.3

Depends on hledger 1.3.

hledger-api 1.3

Depends on hledger 1.3.

2017/3/31 hledger 1.2

new commands list, more powerful balancesheet/incomestatement/cashflow commands, more parseable print output, better --pivot, basic automated postings and periodic transactions support, more and easier addons, bugfixes

Release contributors: Simon Michael, Mykola Orliuk, Justin Le, Peter Simons, Stefano Rodighiero, Moritz Kiefer, Pia Mancini, Bryan Richter, Steven R. Baker, Hans-Peter Deifel, Joshua Chia, Joshua Kehn, Michael Walker.

project-wide changes 1.2

Packaging

bump stack config to latest lts, bump brick to 0.15.2 to allow hledger-iadd install in hledger dir, update cabal files to latest hpack 0.17.0/stack 1.4 format (#512), use more accurate license tag in Cabal file (Peter Simons).

Finance

set up a hledger open collective (http://opencollective.com/hledger), more devguide links to issues with bounties, codefund link, start tracking and publishing project finances (dogfooding!).

Documentation and website

docs page & manual cleanups, begin organising a cookbook, update addons list, move detailed addon docs out of hledger manual, document addons installation, explain print's CSV output, note an issue with balance assertions & multiple -f options, clarify tags, add github stars widget to home and devguide, improve market price docs, ui & web screenshots layout fixes, fix extra whitespace after synopsis in hledger-web text manuals, update accounts directive/budget/rewrite/read-related mockups, drop old org notes.

Examples

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

Tools

Travis CI now checks functional tests/build warnings/addons, temporary workaround for Appveyor CI failures, remove accidentally committed pandoc executables, some pandoc filter fixes, mailmap file to clean up git log authors, bench.hs cleanup, fix gitignore of generated manuals, avoid excessive rebuilding with make [func]test, run functional tests more verbosely, add alex/happy update step to cabal-install.sh.

hledger-lib 1.2

journal format

A pipe character can optionally be used to delimit payee names in transaction descriptions, for more accurate querying and pivoting by payee. Eg, for a description like payee name | additional notes, the two parts will be accessible as pseudo-fields/tags named payee and note.

Some journal parse errors now show the range of lines involved, not just the first.

ledger format

The experimental ledger: reader based on the WIP ledger4 project has been disabled, reducing build dependencies.

Misc

Fix a bug when tying the knot between postings and their parent transaction, reducing memory usage by about 10% (#483) (Mykola Orliuk)

Fix a few spaceleaks (#413) (Moritz Kiefer)

Add Ledger.Parse.Text to package.yaml, fixing a potential build failure.

Allow megaparsec 5.2 (#503)

Rename optserror -> usageError, consolidate with other error functions

hledger cli 1.2

CLI

"hledger" and "hledger -h" now print a better organised commands list and general usage message respectively (#297).

The common reporting flags can now be used anywhere on the command line.

Fixed deduplication of addons in commands list.

Fixed ugly stack traces in command line parse error messages.

The -V/--value flag is now a global report flag, so it works with balance, print, register, balancesheet, incomestatement, cashflow, etc. (Justin Le)

The --pivot global reporting option replaces all account names with the value of some other field or tag. It has been improved, eg:

  • we don't add the field/tag name name as a prefix
  • when pivoting on a tag, if the tag is missing we show a blank (rather than showing mixed tag values and account names)
  • a pipe character delimiter may be used in descriptions to get a more accurate and useful payee report (hledger balance --pivot payee)

options cleanups

Addons

Easier installation: move add-ons and example scripts to bin/, convert to stack scripts, add a build script to install all deps, add some functional tests, test add-ons with Travis CI, add installation docs to download page.

Improved docs: all addons now contain their own documentation. Most of them (all but hledger-budget) use a new reduced-boilerplate declaration format and can show short (-h) and long (--help) command line help. (Long help is declared with pre and postambles to the generated options help, short help is that truncated at the start of the hledger common flags.)

hledger now shows a cleaner list of addon commands, showing only the compiled version of an addon when both source and compiled versions are in $PATH. (Addons with .exe extension or no extension are considered compiled. Modification time is not checked, ie, an old compiled addon will override a newer source version. If there are three or more versions of an addon, all are shown. )

New addons added/included:

  • autosync - example symlink to ledger-autosync
  • budget - experimental budget reporting command supporting Ledger-like periodic transactions and automated transactions (Mykola Orliuk)
  • chart - pie-chart-generating prototype, a repackaging of the old hledger-chart tool
  • check - more powerful balance assertions (Michael Walker)
  • check-dupes - find accounts sharing the same leaf name (Stefano Rodighiero)
  • prices - show all market price records (Mykola Orliuk)
  • register-match - a helper for ledger-autosync's deduplication, finds best match for a transaction description

The equity command now always generates a valid journal transaction, handles prices better, and adds balance assertions (Mykola Orliuk).

The rewrite command is more robust and powerful (Mykola Orliuk):

  • in addition to command-line rewrite options, it understands rewrite rules defined in the journal, similar to Ledger's automated transactions (#99). Eg:

    = ^income
        (liabilities:tax)  *.33
    
    = expenses:gifts
        budget:gifts  *-1
        assets:budget  *1
    
  • it can generate diff output, allowing easier review of the proposed changes, and safe modification of original journal files (preserving file-level comments and directives). Eg:

    hledger-rewrite --diff Agency --add-posting 'Expenses:Taxes  *0.17' | patch
    
  • rewrites can affect multiple postings in a transaction, not just one.

  • posting-specific dates are handled better

balance

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

balancesheet/cashflow/incomestatement

These commands are now more powerful, able to show multicolumn reports and generally having the same features as the balance command. (Justin Le)

balancesheet has always ignored a begin date specified with a -b or -p option; now it also ignores a begin date specified with a date: query. (Related discussion at #531)

The output of print is now always a valid journal (fixes #465) (Mykola Orliuk).

print now tries to preserves the format of implicit/explicit balancing amounts and prices, by default. To print with all amounts explicit, use the new --explicit/-x flag (fixes #442). (Mykola Orliuk)

Don't lose the commodity of zero amounts/zero balance assertions (fixes #475) (Mykola Orliuk)

Misc

Fix a regression in the readability of option parsing errors (#478) (Hans-Peter Deifel)

Fix an example in Cli/Main.hs (Steven R. Baker)

Allow megaparsec 5.2 (#503)

hledger-ui 1.2

Fix a pattern match failure when pressing E on the transaction screen (fixes #508)

Accounts with ? in name had empty registers (fixes #498) (Bryan Richter)

Allow brick 0.16 (Joshua Chia) and brick 0.17/vty 0.15 (Peter Simons)

Allow megaparsec 5.2 (fixes #503)

Allow text-zipper 0.10

hledger-web 1.2

Accounts with ? in name had empty registers (fixes #498) (Bryan Richter)

Allow megaparsec 5.2 (fixes #503)

2016/12/31 hledger 1.1

more robust file format detection, integration of WIP ledger4 parser, balance assignments, hledger-ui --watch, hledger-iadd integration, bugfixes

Release contributors: Simon Michael, Johannes Gerer, Mykola Orliuk, Shubham Lagwankar.

project-wide changes 1.1

misc

  • don't show stack trace details in errors

  • more predictable file format detection

    When we don't recognise a file's extension, instead of choosing a subset of readers to try based on content sniffing, now we just try them all. Also, this can be overridden by prepending the reader name and a colon to the file path (eg timedot:file.dat, csv:-).

  • avoid creating junk CSV rules files when trying alternate readers. We now create it only after successfully reading a file as CSV.

  • improvements to -B and -V docs: clearer descriptions, more linkage (#403)

hledger-lib 1.1

journal format

  • balance assignments are now supported (#438, #129, #157, #288)

    This feature also brings a slight performance drop (~5%); optimisations welcome.

  • also recognise *.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.

misc

  • update base lower bound to enforce GHC 7.10+

    hledger-lib had a valid install plan with GHC 7.8, but currently requires GHC 7.10 to compile. Now we require base 4.8+ everywhere to ensure the right GHC version at the start.

  • Hledger.Read api cleanups

  • rename dbgIO to dbg0IO, consistent with dbg0, and document a bug in dbg*IO

  • make readJournalFiles [f] equivalent to readJournalFile f (#437)

  • more general parser types enabling reuse outside of IO (#439)

hledger cli 1.1

balance

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

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

misc

  • fix non-existent "oldtime" dependency (#431)

  • hledger-equity.hs now generates valid journal format when there are multiple commodities

hledger-ui 1.1

  • with --watch, the display updates automatically to show file or date 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 127.0.0.1 being hardcoded. That can now be changed with --host=IPADDR. Also, the default base url uses this address rather than a hard-coded "localhost".

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

(mail)

Release contributors: Simon Michael, Dominik Süß, Thomas R. Koll, Moritz Kiefer, jungle-boogie, Sergei Trofimovich, Malte Brandy, Sam Doshi, Mitchell Rosen, Hans-Peter Deifel, Brian Scott, and Andrew Jones.

misc

  • added GHC 8 support, dropped GHC 7.6 and 7.8 support.

    GHC 7.8 support could be restored with small code changes and a maintainer.

  • a cabal.project file has been added (Moritz Kiefer)

  • use hpack for maintaining cabal files (#371).

    Instead of editing cabal files directly, we now edit the less verbose and less redundant package.yaml files and let stack (or hpack) update the cabal files. We commit both the .yaml and .cabal files.

  • clean up some old cabal flags

  • tools/simplebench has been spun off as the quickbench package.

  • add Appveyor CI builds, provide more up-to-date Windows binaries

  • extra: add a bunch of CSV rules examples

docs

  • the website is simpler, clearer, and more mobile-friendly.

    Docs are now collected on a single page and organised by type: getting started, reference, more.

  • reference docs have been split into one manual for each executable and file format.

    This helps with maintenance and packaging and also should make it easier to see what's available and to read just what you need.

  • manuals are now provided in html, plain text, man and info formats

    generated from the same source by a new Shake-based docs build system. (#292)

  • versioned manuals are provided on the website, covering recent releases and the latest dev version (#385, #387)

  • manuals are built in to the hledger executables, allowing easy offline reading on all platforms.

    PROG -h              shows PROG's command-line usage
    PROG --help          shows PROG's manual (fixed width)
    PROG --man           shows PROG's manual with man (formatted/paged)
    PROG --info          shows PROG's manual with info (hypertext)
    hledger help [TOPIC] shows any manual
    hledger man  [TOPIC] shows any manual with man
    hledger info [TOPIC] shows any manual with info
    
  • the general and reporting options are now listed in all executable manuals.

    We assume any of them which are unsupported are harmlessly ignored.

  • demo.hledger.org is using beancount's example journal.

    This is the somewhat realistic example journal from the beancount project, tweaked for hledger.

  • minor copyedits (jungle-boogie)

cli

  • parsing multiple input files is now robust.

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

misc

  • file parsers have been ported from Parsec to Megaparsec \o/ (#289, #366) (Alexey Shmalko, Moritz Kiefer)

  • most hledger types have been converted from String to Text, reducing memory usage by 30%+ on large files

  • file parsers have been simplified for easier troubleshooting (#275).

    The journal/timeclock/timedot parsers, instead of constructing opaque journal update functions which are later applied to build the journal, now construct the journal directly by modifying the parser state. This is easier to understand and debug. It also rules out the possibility of journal updates being a space leak. (They weren't, in fact this change increased memory usage slightly, but that has been addressed in other ways). The ParsedJournal type alias has been added to distinguish "being-parsed" journals and "finalised" journals.

  • file format detection is more robust.

    The Journal, Timelog and Timedot readers' detectors now check each line in the sample data, not just the first one. I think the sample data is only about 30 chars right now, but even so this fixed a format detection issue I was seeing. Also, we now always try parsing stdin as journal format (not just sometimes).

  • all file formats now produce transaction ids, not just journal (#394)

  • git clone of the hledger repo on windows now works (#345)

  • added missing benchmark file (#342)

  • our stack.yaml files are more compatible across stack versions (#300)

  • use newer file-embed to fix ghci working directory dependence

  • report more accurate dates in account transaction report when postings have their own dates

    (affects hledger-ui and hledger-web registers). The newly-named "transaction register date" is the date to be displayed for that transaction in a transaction register, for some current account and filter query. It is either the transaction date from the journal ("transaction general date"), or if postings to the current account and matched by the register's filter query have their own dates, the earliest of those posting dates.

  • simplify account transactions report's running total.

    The account transactions report used for hledger-ui and -web registers now gives either the "period total" or "historical total", depending strictly on the --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

add

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

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

balance

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

balancesheet

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

register

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

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

stats

  • fixed an issue with ordering of include files

misc

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

misc

  • H toggles between showing "historical" or "period" balances (#392).

    By default hledger-ui now shows historical balances, which include transactions before the report start date (like hledger balance --historical). Use the H key to toggle to "period" mode, where balances start from 0 on the report start date.

  • shift arrow keys allow quick period browsing

    • shift-down narrows to the next smaller standard period (year/quarter/month/week/day), shift-up does the reverse
    • when narrowed to a standard period, shift-right/left moves to the next/previous period
    • `t` sets the period to today.
  • a runs the add command

  • E runs $HLEDGERUIEDITOR or $EDITOR or a default editor (vi) on the journal file.

    When using emacs or vi, if a transaction is selected the cursor will be positioned at its journal entry.

  • / key sets the filter query; BACKSPACE/DELETE clears it

  • Z toggles display of zero items (like --empty), and they are shown by default.

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

ui

  • use full width on large screens, hide sidebar on small screens, more standard bootstrap styling (#418, #422) (Dominik Süß)

  • show the sidebar by default (#310)

  • fix the add link's tooltip

  • when the add form opens, focus the first field (#338)

  • leave the add form's date field blank, avoiding a problem with tab clearing it (#322)

  • use transaction id instead of date in transaction urls (#308) (Thomas R. Koll)

  • after following a link to a transaction, highlight it (Thomas R. Koll)

  • misc. HTML/CSS/file cleanups/fixes (Thomas R. Koll)

misc

  • startup is more robust (#226).

    Now we exit if something is already using the specified port, and we don't open a browser page before the app is ready.

  • termination is more robust, avoiding stray background threads.

    We terminate the server thread more carefully on exit, eg on control-C in GHCI.

  • more robust register dates and filtering in some situations (see hledger-ui notes)

  • reloading the journal preserves options, arguments in effect (#314).

    The initial query specified by command line arguments is now preserved when the journal is reloaded. This does not appear in the web UI, it's like an invisible extra filter.

  • show a proper not found page on 404

  • document the special `inacct:` query (#390)

hledger-api 1.0

misc

  • new hledger-api tool: a simple web API server with example clients (#316)

  • start an Angular-based API example client (#316) (Thomas R. Koll)

2008-2015 Pre-1.0

2015/10/30 hledger 0.27

New curses-style interface, market value reporting, wide characters, fast regex aliases, man pages (mail)

Release contributors: Simon Michael, Carlos Lopez-Camey.

hledger 0.27:

Account aliases:

  • Regular expression account aliases are now fast enough that you can use lots of them without slowing things down. They now take O(aliases x accounts) time, instead of O(aliases x transactions); also, regular expressions are no longer recompiled unnecessarily.

Documentation:

  • The hledger packages now have man pages, based on the current user manual, thanks to the mighty pandoc (#282).

Journal format:

  • Dates must now begin with a digit (not /, eg).

  • The comment directive longer requires an end comment, and will extend to the end of the file(s) without it.

Command-line interface:

  • Output (balance reports, register reports, print output etc.) containing wide characters, eg chinese/japanese/korean characters, should now align correctly, when viewed in apps and fonts that show wide characters as double width (#242).

  • The argument for --depth or depth: must now be positive.

add:

  • Journal entries are now saved with all amounts explicit, to avoid losing price info (#283).

  • Fixed a bug which sometimes (when the same letter pair was repeated) caused it not to pick the most similar past transaction for defaults.

balance:

  • There is now a -V/--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.

print:

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

register:

  • Amount columns now resize automatically, using more space if it's needed and available.

hledger-ui 0.27:

  • hledger-ui is a new curses-style UI, intended to be a standard part of the hledger toolset for all users (except on native MS Windows, where the vty lib is not yet supported).

    The UI is quite simple, allowing just browsing of accounts and transactions, but it has a number of improvements over the old hledger-vty, which it replaces:

    • adapts to screen size
    • handles wide characters
    • shows multi-commodity amounts on one line
    • manages cursor and scroll position better
    • allows depth adjustment
    • allows --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.

Queries:

  • real: (no argument) is now a synonym for real:1

  • tag: now matches tag names with a regular expression, like most other queries

  • empty: is no longer supported, as it overlaps a bit confusingly with amt:0. The --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:

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

add:

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

balancesheet:

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

print:

  • We now limit the display precision of inferred prices. (#262)

    When a transaction posts to two commodities without specifying the conversion price, we generate a price which makes it balance (cf <http://hledger.org/journal.html#transaction-prices). The print command showed this with full precision (so that manual calculations with the displayed numbers would look right), but this sometimes meant we showed 255 digits (when there are multiple postings in the commodity being priced, and the averaged unit price is an irrational number). In this case we now set the price's display precision to the sum of the (max) display precisions of the commodities involved. An example:

    hledger -f- print
    <<<
    1/1
        c    C 10.00
        c    C 11.00
        d  D -320.00
    >>>
    2015/01/01
        c  C 10.00 @ D 15.2381
        c  C 11.00 @ D 15.2381
        d     D -320.00
    
    >>>=0
    

    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.

register:

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

stats:

  • stats now supports -o/--outputfile, like register/balance/print.
  • An O(n^2) performance slowdown has been fixed, it's now much faster on large journals.
    +--------------------------------------++--------+--------+
    |                                      ||   0.25 |   0.26 |
    +======================================++========+========+
    | -f data/100x100x10.journal     stats ||   0.10 |   0.16 |
    | -f data/1000x1000x10.journal   stats ||   0.45 |   0.21 |
    | -f data/10000x1000x10.journal  stats ||  58.92 |   2.16 |
    +--------------------------------------++--------+--------+
    

Miscellaneous:

  • The June 30 day span was not being rendered correctly; fixed. (#272)
  • The deprecated shakespeare-text dependency has been removed more thoroughly.
  • The bench script invoked by "cabal bench" or "stack bench" now runs some simple benchmarks. You can get more accurate benchmark times by running with --criterion. This will usually give much the same numbers and takes much longer. Or with --simplebench, it benchmarks whatever commands are configured in bench/default.bench. This mode uses the first "hledger" executable in $PATH.

User-visible changes in hledger-web since 0.25.1:

  • make the j keybinding respect --base-url (fixes #271)
  • respect command line options (fixes #225)
  • include the unminified jquery source again (#161)
  • fix build breakage from #165 (fixes #268)
  • fix a js error breaking add form in browsers other than firefox (fixes #251)
  • drop deprecated network-conduit dependency
2015/4/29 hledger-web 0.25.1
  • support/require base-compat >0.8 (#245)
2015/4/29 hledger 0.25.1
  • timelog: support the description field (#247)
2015/4/29 hledger-lib 0.25.1
  • support/require base-compat >0.8 (#245)

2015/4/7 hledger 0.25

GHC 7.10 compatibility, terminal width awareness, useful averages and totals columns, and a more robust hledger-web add form.

mail

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
Examples:
    $ hledger reg                 # use terminal width on posix
    $ hledger reg -w 100          # width 100, equal description/account widths
    $ hledger reg -w 100,40       # width 100, wider description
    $ hledger reg -w $COLUMNS,100 # terminal width and set description width
  • balance: new -T/--row-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:

General:

  • fix redundant compilation when cabal installing the hledger packages
  • switch to Decimal for representing amounts (#118)
  • report interval headings (eg in balance, register reports) are shown compactly when possible
  • general speedups.
+--------------------------------------------++----------------+--------------+--------+
|                                            || hledger-0.23.3 | hledger-0.24 | ledger |
+============================================++================+==============+========+
| -f data/100x100x10.journal     balance     ||           0.05 |         0.03 |   0.01 |
| -f data/1000x1000x10.journal   balance     ||           0.34 |         0.21 |   0.04 |
| -f data/10000x1000x10.journal  balance     ||           2.72 |         1.48 |   0.19 |
| -f data/10000x1000x10.journal  balance aa  ||           3.16 |         1.55 |   0.14 |
| -f data/100x100x10.journal     register    ||           0.09 |         0.05 |   0.04 |
| -f data/1000x1000x10.journal   register    ||           0.66 |         0.32 |   0.30 |
| -f data/10000x1000x10.journal  register    ||           6.27 |         2.77 |   2.80 |
| -f data/10000x1000x10.journal  register aa ||           3.30 |         1.62 |   0.21 |
| -f data/100x100x10.journal     print       ||           0.06 |         0.05 |   0.01 |
| -f data/1000x1000x10.journal   print       ||           0.42 |         0.25 |   0.04 |
| -f data/10000x1000x10.journal  print       ||           3.95 |         2.57 |   0.38 |
| -f data/10000x1000x10.journal  print aa    ||           3.23 |         1.56 |   0.14 |
| -f data/100x100x10.journal     stat        ||           0.04 |         0.03 |   0.01 |
| -f data/1000x1000x10.journal   stat        ||           0.35 |         0.24 |   0.03 |
| -f data/10000x1000x10.journal  stat        ||          14.84 |        13.29 |   0.20 |
| -f data/10000x1000x10.journal  stat aa     ||          12.08 |        10.16 |   0.17 |
+--------------------------------------------++----------------+--------------+--------+

Journal format:

  • detect decimal point and digit groups more robustly (#196)
  • check that transaction dates are followed by whitespace or newline
  • check that dates use a consistent separator character
  • balance assertions now are specific to a single commodity, like Ledger (#195)
  • support multi-line comments using "comment", "end comment" directives, like Ledger

CSV format:

  • fix: reading CSV data from stdin now works better
  • the original order of same-day transactions is now usually preserved (if the records appear to be in reverse date order, we reverse them before finally sorting by transaction date)
  • the rules file include directive is now relative to the current file's directory (#198)
  • CSV output is now built in to the balance, print, and register commands, controlled by -O/--output-format (and -o/--output-file, see below). This means that hledger data can be easily exported, eg for spreadsheet reporting or to migrate to a different tool.

CLI:

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

Queries:

  • date:/date2: with a malformed date now reports an error instead of being ignored
  • amt: now supports >= or <=
  • clarify status: docs and behaviour; "*" is no longer a synonym for "1" (fixes #227)

balance:

  • fix: in tree mode, --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

register:

  • runs faster with large output
  • supports date2:, and date:/date2: combined with --date2, better (fixes #201, #221, #222)
  • a depth limit of 0 now shows summary items (see balance)
  • -A/--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:

General:

  • fix: add missing hs/js files to package
  • the web UI has been streamlined, dropping the raw and entries views and the edit form
  • the help dialog has been improved
  • keyboard shortcuts are now available
  • the sidebar can be toggled open or closed (press s)

Journal view:

  • layout tweaks for less truncation of descriptions and account names

Register view:

  • fix: don't show all zero amounts when searching by account within an account register view
  • chart improvements: show zero balances with correct commodity; show accurate balance at all dates; show transaction events & tooltips; show zero/today lines & background colors

Add form:

  • parses data more strictly and gives better errors (eg #194)
  • allows any number of postings, not just two
  • after adding a transaction, goes back to the journal
  • keyboard shortcut (a) allows quick access

Dependencies:

  • allow warp 3*, wai-handler-launch 3*
  • require yesod 1.4* (fixes #212)
  • js updated (jquery, bootstrap, flot), added (typeahead, cookie, hotkeys), removed (select2)

API-ish changes in hledger-lib since 0.23.3:

  • fix combineJournalUpdates folding order
  • fix a regexReplaceCI bug
  • fix a splitAtElement bug with adjacent separators
  • mostly replace slow regexpr with regex-tdfa (fixes #189)
  • use the modern Text.Parsec API
  • allow transformers 0.4*
  • regexReplace now supports backreferences
  • Transactions now remember their parse location in the journal file
  • export Regexp types, disambiguate CsvReader's similarly-named type
  • export failIfInvalidMonth/Day (closes #216)
  • track the commodity of zero amounts when possible (useful eg for hledger-web's multi-commodity charts)
  • show posting dates in debug output
  • more debug helpers
2014/9/12 hledger-web 0.23.3
  • remove warp, wai-handler-launch upper bounds (fixes #205)
2014/9/12 hledger 0.23.3
  • allow text 1.2+ (fixes #207)
2014/5/8 hledger 0.23.2
  • register: also fix date sorting of postings (#184)
2014/5/7 hledger 0.23.1
  • register: fix a refactoring-related regression that the tests missed: if transactions were not ordered by date in the journal, register could include postings before the report start date in the output. (#184)
  • add: don't apply a default commodity to amounts on entry (#138)
  • cli: options before the add-on command name are now also passed to it (#182)
  • csv: allow the first name in a fields list to be empty (#178)
  • csv: don't validate fields count in skipped lines (#177)

2014/5/1 hledger 0.23

command-line fixes and polish, a new accounts command, and a number of changes to the balance command relating to --depth, --flat, and multicolumn mode, which I find has made it much more useful. mail

Changes since 0.22.2:

Journal format:

  • A # (hash) in column 0 is now also supported for starting a top-level journal comment, like Ledger.
  • The "too many missing amounts" error now reminds about the 2-space rule.
  • Fix: . (period) is no longer parsed as a valid amount.
  • Fix: default commodity directives no longer limit the maximum display precision (#169).
  • Fix: + before an amount is no longer parsed as part of the commodity (#181).

CLI:

  • Command-line help cleanups, layout improvements.
  • Descriptions are shown for known add-ons in the command list.
  • Command aliases have been simplified.
  • Add-ons can now have any of these file extensions: none, hs, lhs, pl, py, rb, rkt, sh, bat, com, exe.
  • Add-ons are displayed without their file extensions when possible.
  • Add-ons with the same name as a built-in command or alias are ignored.
  • Fix: add-on detection and invocation now works on windows.
  • Fix: add-ons with digits in the name are now found.
  • Fix: add-on arguments containing a single quote now work.
  • Fix: when -- is used to hide add-on options from the main program, it is no longer passed through as an add-on argument.

accounts:

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

add:

  • Tab completion now works at all prompts, and will insert the default if the input area is empty.
  • Account and amount defaults are more robust and useful.
  • Transactions may also be completed by the enter key, when there are no more default postings.
  • Input prompts are displayed in a different colour when supported.

balance:

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

print:

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

register:

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

Queries:

  • The currency/commodity query prefix (sym:) has been renamed to cur:.
  • Currency/commodity queries are applied more strongly in register and balance reports, filtering out unwanted currencies entirely. Eg hledger balance cur:'$' now reports only the dollar amounts even if there are multi-currency transactions or postings.
  • Amount queries like amt:N, amt:N, where N is not 0, now do an unsigned comparison of the amount and N. That is, they compare the absolute magnitude. To do a signed comparison instead, write N with its sign (eg amt:+N, amt:<+N, amt:>-N).
  • Fix: amount queries no longer give false positives on multi-commodity amounts.

Miscellaneous:

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

New:

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

Improved:

  • web: include unminified source of all javascript to help packagers (fixes #161)
  • web: work around clang-related build failures with OS X mavericks/XCode 5
  • web: allow blaze-html 0.7 (closes #159)
2014/1/6 hledger 0.22.1
  • require the latest pretty-show so hledger installation no longer needs an upgraded version of happy, and the docs build on hackage

  • require regex-tdfa directly instead of regex-compat-tdfa, simplifying Debian packaging

2013/12/13 hledger 0.22

mail

New:

  • balance: with a reporting interval (monthly, yearly etc.), the balance command will now show a multi-column report, showing either the per-period changes in balance (by default), the period ending balances starting from zero (--cumulative), or the actual period ending balances (--historical). A more detailed specification of the balance command's behaviour has been added to Hledger.Cli.Balance.

  • csv: rules files can now include other rules files, useful for factoring out common rules

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

Improved:

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

Fixed:

  • csv: CSV data containing non-ascii characters is now supported

  • build with latest versions of dependencies (text, warp, http-conduit etc.)

Release contributors:

Marko Kocić, Max Bolingbroke, and a big welcome to first-time committer John Wiegley! :)

2013/7/10 hledger-web 0.21.3
  • drop yesod-platform dependency, it is not worthwhile. The other yesod dependencies are currently without version ranges, so cabal install might require --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 0.20.0.1
  • web: require at least version 1.1.7 of yesod-core to avoid a potential build error
  • Update the bug tracker and source repository links on hackage

2013/5/1 hledger 0.20

Bugs fixed:

  • balance: a 0.19 regression which showed wrong total balance with --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

Querying:

  • Use code: to match the transaction code (check number) field
  • Use amt: followed by <, = or > and a number N to match amounts by magnitude. 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

Add-ons:

  • The hledger-interest and hledger-irr commands have been released/updated.
  • hledger-chart and hledger-vty remain unmaintained and deprecated.

Documentation and infrastructure:

  • The hledger docs and website have been reorganised and updated
  • Manuals for past releases are provided as well as the latest dev version
  • hledger has moved from darcs and darcs hub to git and github (!)
  • The bug tracker has moved from google code to github
  • Feature requests and project planning are now managed on trello
  • A build bot builds against multiple GHC versions on each commit

Release contributors:

  • Sascha Welter commissioned register enhancements (--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

mail

  • 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

mail

  • 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

mail

  • 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

mail

  • 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

mail

  • 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

mail

  • 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

mail

  • 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

mail

  • 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

mail

  • 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

Report examples

Some simple reports on the essentials page.

Some example reports with an adapted copy of this example journal from beancount:

A yearly income statement, summarised to depth 3, sorted by amount:

$ hledger -f examples/bcexample.hledger is -Y -3 -S
Income Statement 2012-01-01..2014-10-11

                                ||                                     2012                                      2013                                      2014 
================================++==============================================================================================================================
 Revenues                       ||                                                                                                                              
--------------------------------++------------------------------------------------------------------------------------------------------------------------------
 Income:US:Federal              ||                          17000.00 IRAUSD                           17500.00 IRAUSD                           17500.00 IRAUSD 
 Income:US:Hoogle               ||              129132.20 USD, 120.12 VACHR               129382.20 USD, 120.12 VACHR                106183.70 USD, 97.02 VACHR 
 Income:US:ETrade               ||                                        0                                114.42 USD                                258.92 USD 
--------------------------------++------------------------------------------------------------------------------------------------------------------------------
                                || 17000.00 IRAUSD, 129132.20 USD, 1 more..  17500.00 IRAUSD, 129496.62 USD, 1 more..  17500.00 IRAUSD, 106442.62 USD, 1 more.. 
================================++==============================================================================================================================
 Expenses                       ||                                                                                                                              
--------------------------------++------------------------------------------------------------------------------------------------------------------------------
 Expenses:Home:Rent             ||                             28800.00 USD                              28800.00 USD                              21600.00 USD 
 Expenses:Food:Restaurant       ||                              4517.54 USD                               4286.23 USD                               4164.76 USD 
 Expenses:Food:Groceries        ||                              2188.90 USD                               2222.97 USD                               1602.51 USD 
 Expenses:Transport:Tram        ||                              1320.00 USD                               1320.00 USD                               1080.00 USD 
 Expenses:Health:Vision         ||                              1099.80 USD                               1099.80 USD                                888.30 USD 
 Expenses:Home:Internet         ||                               960.48 USD                                959.82 USD                                720.50 USD 
 Expenses:Home:Electricity      ||                               780.00 USD                                780.00 USD                                585.00 USD 
 Expenses:Health:Medical        ||                               711.88 USD                                711.88 USD                                574.98 USD 
 Expenses:Health:Life           ||                               632.32 USD                                632.32 USD                                510.72 USD 
 Expenses:Financial:Commissions ||                                35.80 USD                                214.80 USD                                 89.50 USD 
 Expenses:Health:Dental         ||                                75.40 USD                                 75.40 USD                                 60.90 USD 
 Expenses:Financial:Fees        ||                                48.00 USD                                 48.00 USD                                 40.00 USD 
 Expenses:Food:Coffee           ||                                16.28 USD                                 19.79 USD                                 47.65 USD 
 Expenses:Food:Alcohol          ||                                        0                                 22.35 USD                                         0 
 Expenses:Taxes:Y2013           ||                                        0             17500.00 IRAUSD, 51477.20 USD                                859.09 USD 
 Expenses:Taxes:Y2014           ||                                        0                                         0             17500.00 IRAUSD, 41836.20 USD 
 Expenses:Taxes:Y2012           ||            17000.00 IRAUSD, 51477.20 USD                                917.43 USD                                         0 
--------------------------------++------------------------------------------------------------------------------------------------------------------------------
                                ||            17000.00 IRAUSD, 92663.60 USD             17500.00 IRAUSD, 93587.99 USD             17500.00 IRAUSD, 74660.11 USD 
================================++==============================================================================================================================
 Net:                           ||               36468.60 USD, 120.12 VACHR                35908.63 USD, 120.12 VACHR                 31782.51 USD, 97.02 VACHR 

Yearly balance sheets, with commodities converted to their year-end value in USD where possible:

$ hledger -f examples/bcexample.hledger bs -Y -V --infer-value
Balance Sheet 2012-12-31..2014-12-31, valued at period ends

                            ||                 2012-12-31                  2013-12-31                   2014-12-31 
============================++=====================================================================================
 Assets                     ||                                                                                     
----------------------------++-------------------------------------------------------------------------------------
 Assets:US:BofA:Checking    ||                7448.62 USD                 7247.12 USD                   596.05 USD 
 Assets:US:ETrade:Cash      ||                 337.18 USD                  239.06 USD                  5120.50 USD 
 Assets:US:ETrade:GLD       ||                          0                 6213.90 USD                  6592.60 USD 
 Assets:US:ETrade:ITOT      ||                1364.90 USD                 4021.92 USD                  2648.26 USD 
 Assets:US:ETrade:VEA       ||                1411.56 USD                 2414.28 USD                  4290.84 USD 
 Assets:US:ETrade:VHT       ||                4695.80 USD                 5456.00 USD                 14782.32 USD 
 Assets:US:Hoogle:Vacation  ||               120.12 VACHR                240.24 VACHR                 337.26 VACHR 
 Assets:US:Vanguard:Cash    ||                  -0.04 USD                           0                    -0.02 USD 
 Assets:US:Vanguard:RGAGX   ||               15595.79 USD                28532.66 USD                 41391.57 USD 
 Assets:US:Vanguard:VBMPX   ||               10037.03 USD                22449.53 USD                 33769.05 USD 
----------------------------++-------------------------------------------------------------------------------------
                            || 40890.84 USD, 120.12 VACHR  76574.46 USD, 240.24 VACHR  109191.17 USD, 337.26 VACHR 
============================++=====================================================================================
 Liabilities                ||                                                                                     
----------------------------++-------------------------------------------------------------------------------------
 Liabilities:US:Chase:Slate ||                1366.52 USD                 1906.01 USD                  2891.85 USD 
----------------------------++-------------------------------------------------------------------------------------
                            ||                1366.52 USD                 1906.01 USD                  2891.85 USD 
============================++=====================================================================================
 Net:                       || 39524.32 USD, 120.12 VACHR  74668.45 USD, 240.24 VACHR  106299.32 USD, 337.26 VACHR 

The same reports as HTML:

$ hledger -f examples/bcexample.hledger is -Y -3 -S -o is.html
$ hledger -f examples/bcexample.hledger bs -Y -V --infer-value -o bs.html
$ open is.html bs.html

Income Statement 2012-01-01..2014-10-11

201220132014
 
Revenues
17000.00 IRAUSD17500.00 IRAUSD17500.00 IRAUSD
129132.20 USD, 120.12 VACHR129382.20 USD, 120.12 VACHR106183.70 USD, 97.02 VACHR
0114.42 USD258.92 USD
Total:17000.00 IRAUSD, 129132.20 USD, 120.12 VACHR17500.00 IRAUSD, 129496.62 USD, 120.12 VACHR17500.00 IRAUSD, 106442.62 USD, 97.02 VACHR
 
Expenses
28800.00 USD28800.00 USD21600.00 USD
4517.54 USD4286.23 USD4164.76 USD
2188.90 USD2222.97 USD1602.51 USD
1320.00 USD1320.00 USD1080.00 USD
1099.80 USD1099.80 USD888.30 USD
960.48 USD959.82 USD720.50 USD
780.00 USD780.00 USD585.00 USD
711.88 USD711.88 USD574.98 USD
632.32 USD632.32 USD510.72 USD
35.80 USD214.80 USD89.50 USD
75.40 USD75.40 USD60.90 USD
48.00 USD48.00 USD40.00 USD
16.28 USD19.79 USD47.65 USD
022.35 USD0
017500.00 IRAUSD, 51477.20 USD859.09 USD
0017500.00 IRAUSD, 41836.20 USD
17000.00 IRAUSD, 51477.20 USD917.43 USD0
Total:17000.00 IRAUSD, 92663.60 USD17500.00 IRAUSD, 93587.99 USD17500.00 IRAUSD, 74660.11 USD
 
Net:36468.60 USD, 120.12 VACHR35908.63 USD, 120.12 VACHR31782.51 USD, 97.02 VACHR


Balance Sheet 2012-12-31..2014-12-31, valued at period ends

201220132014
 
Assets
7448.62 USD7247.12 USD596.05 USD
337.18 USD239.06 USD5120.50 USD
06213.90 USD6592.60 USD
1364.90 USD4021.92 USD2648.26 USD
1411.56 USD2414.28 USD4290.84 USD
4695.80 USD5456.00 USD14782.32 USD
120.12 VACHR240.24 VACHR337.26 VACHR
-0.04 USD0-0.02 USD
15595.79 USD28532.66 USD41391.57 USD
10037.03 USD22449.53 USD33769.05 USD
Total:40890.84 USD, 120.12 VACHR76574.46 USD, 240.24 VACHR109191.17 USD, 337.26 VACHR
 
Liabilities
1366.52 USD1906.01 USD2891.85 USD
Total:1366.52 USD1906.01 USD2891.85 USD
 
Net:39524.32 USD, 120.12 VACHR74668.45 USD, 240.24 VACHR106299.32 USD, 337.26 VACHR

Reporting version control stats

You can export version control history as a journal, for analysis with hledger. Here's an example: comparing the big 3 PTA apps' git commit counts.

Start a journal with some account declarations; this is just to set the projects' display order:

printf "account ledger\naccount hledger\naccount beancount\n" >project-commits.j

Export each git commit as a transaction. git log makes this easy; we include the commit date, short hash, and summary:

for P in ledger hledger beancount; do
  git -C ../$P log --reverse --format="%cd (%h) %s%n  ($P)  1%n" --date=short
done >> project-commits.j 

The resulting journal looks like:

account ledger
account hledger
account beancount

2003-09-29 (e95ea133) Initial revision
  (ledger)  1

2003-09-29 (faaed980) *** empty log message ***
  (ledger)  1
...

Now we can run reports:

$ hledger -f project-commits.j  stats
Main file                : /Users/simon/src/PTA/hledger/project-commits.j
Included files           : 
Transactions span        : 2003-09-29 to 2023-12-02 (7369 days)
Last transaction         : 2023-12-01 (0 days ago)
Transactions             : 24966 (3.4 per day)
Transactions last 30 days: 76 (2.5 per day)
Transactions last 7 days : 15 (2.1 per day)
Payees/descriptions      : 19997
Accounts                 : 3 (depth 1)
Commodities              : 1 ()
Market prices            : 0 ()

Run time (throughput)    : 0.70s (35759 txns/s)
$ hledger -f project-commits.j bal -YTN --transpose
Balance changes in 2003-01-01..2023-12-31:

         || ledger  hledger  beancount 
=========++============================
 2003    ||     50        0          0 
 2004    ||    327        0          0 
 2005    ||      0        0          0 
 2006    ||      0        0          0 
 2007    ||      0      150          0 
 2008    ||   1248      407        190 
 2009    ||   1462      610         86 
 2010    ||    437      606         15 
 2011    ||    153      529         25 
 2012    ||    706      350         18 
 2013    ||    708      365        433 
 2014    ||    578      567       1484 
 2015    ||    367      542       1160 
 2016    ||    115      732        686 
 2017    ||     55      892        240 
 2018    ||     78     1155        393 
 2019    ||    130     1089        189 
 2020    ||     90     1313        773 
 2021    ||     42     1131        299 
 2022    ||     55      832        112 
 2023    ||    162      773         57 
   Total ||   6763    12043       6160 
$ for P in ledger hledger beancount; do hledger -f project-commits.j reg -w80 ^$P | head -1; done
2003-09-29 Initial revision     (ledger)                         1             1
2007-01-27 start                (hledger)                        1             1
2008-04-23 Initial import.      (beancount)                      1             1
$ for P in ledger hledger beancount; do hledger -f project-commits.j print ^$P | hledger -f- bal -YATN --summary | tail -4; done

        ||   Total  Average 
========++==================
 ledger ||    6763      322 

         ||   Total  Average 
=========++==================
 hledger ||   12043      708 

           ||   Total  Average 
===========++==================
 beancount ||    6160      385 

Rewrite account names

Here's an example of using account aliases.

Say a sole proprietor has a personal.journal:

2014/1/2
    expenses:food  $1
    assets:cash

and a business.journal:

2014/1/1
    expenses:office supplies  $1
    assets:business checking

So each entity (the business owner, and the business) has their own file with its own simple chart of accounts. However, at tax reporting time we need to view these as a single entity (at least in the US). In unified.journal, we include both files, and rewrite the personal account names to fit into the business chart of accounts,

alias expenses    = equity:draw:personal
alias assets:cash = assets:personal cash
include personal.journal
end aliases

include business.journal

Now we can see the data from both files at once, and the personal account names have changed:

$ hledger -f unified.journal print
2014/01/01                                    # from business.journal - no aliases applied
    expenses:office supplies            $1
    assets:business checking           $-1

2014/01/02                                    # from personal.journal
    equity:draw:personal:food            $1   # <- was expenses:food
    assets:personal cash                $-1   # <- was assets:cash

You can also specify aliases on the command line. This could be useful to quickly rewrite account names when sharing a report with someone else, such as your accountant:

$ hledger --alias 'my earning=income:business' ...

See also Change account name separator.

Rewrite commodity symbols

Three ways to temporarily change a commodity symbol, eg to show "$" as "USD" in a report:

Postprocess

Simplest. Here, we use sed to replace all $ with USD in the output:

$ hledger bal | sed 's/\$/USD /g'

Preprocess

Most powerful. We rewrite the journal file before hledger processes it. You can merge multiple symbols into one this way, eg if inconsistent symbols have been used for a currency:

$ cat $LEDGER_FILE | sed 's/\$/USD /g' | hledger -f- bal

Value conversion

Most portable, requires only hledger. We create a dummy one-to-one market price between the old and new commodity symbols, and use market value reports to convert. This assumes your other market prices, if any, don't interfere.

You can add the market price in the main journal:

$ echo 'P 2000-01-01 $ 1 USD' >> $LEDGER_FILE  # once
$ hledger bal -V

Or in a separate file that you include only when needed:

$ echo 'P 2000-01-01 $ 1 USD' >> rewrite-symbols.j  # once
$ hledger bal -V -f $LEDGER_FILE -f rewrite-symbols.j

Save frequently used options

You can save frequently used options and arguments in an argument file, one per line, then reuse them via a @FILE argument on the command line.

Here's an example. I keep frequently-used options for quick daily reports in a file called simple.args. The name can be anything; I use a .args suffix so I can find these easily. Here's the content of simple.args:

--alias=/:(business|personal):/=:
--alias=/:(bank|cash|online):/=:
--alias=/:bofi:/=:b
--alias=/:unify:/=:u
--alias=/:wf:/=:w
-2
cur:.

The format is one command-line flag or command-line argument per line. Special characters should have one less level of quoting than at the command prompt (so no quotes around those (|) characters), and spaces should be avoided except inside quotes (so = is used between flags and arguments).

Now if I write @simple.args in a hledger command line, it will be replaced by all of the above options/flags.

(In case you're wondering about this example: it removes some detail, giving simplified reports which were easier for me to read at a glance):

  • the aliases simplify the chart of accounts, hiding some distinctions (eg business vs. personal) and flattening some bank account names
  • the -2 depth flag limits account depth to 2, hiding deeper subaccounts
  • the cur:. query argument shows only single-character currencies, hiding a bunch of cluttersome commodities I don't want to see

Usage

Generate a balance report showing the simplified accounts:

$ hledger bal @simple.args

Start a live-updating hledger-ui showing the simplified asset accounts only:

$ hledger-ui --watch @simple.args assets

Options in the arguments file can be overridden by similar options later on the command line, in the usual way. Eg, to show just a little more account detail:

$ hledger bal @simple.args -3

Suppressing this feature

If you actually need to write an argument beginning with @, eg let's say you have an account pattern beginning with that character, you'll want a way to disable this feature. On unix systems at least, you can do that by inserting a -- (double hyphen) argument first. Eg:

$ hledger bal @somewhere.com       # looks for additional arguments in the ./somewhere.com file
$ hledger bal -- @somewhere.com    # matches account names containing "@somewhere.com"

On windows, this double hyphen trick might require a hledger built with GHC 8.2+.

Scripts and add-ons

This document is the README in the hledger repo's bin directory, and is also published as Scripts and add-ons on hledger.org.

Add-on commands are executable script files or compiled programs named hledger-*, which show up in hledger's commands list. Some notable add-ons are listed in the hledger manual.

The rest of this page lists smaller scripts and add-ons which are collected in bin/, grouped by how closely they work with hledger:

To be clear: you don't need any of these when starting out with hledger. hledger comes with many built-in commands, and you may want to get familiar with those first.

These scripts don't use hledger directly, but are complementary and might be useful to hledger users. (plaintextaccounting.org has a longer list of PTA tools.)

paypaljson

paypaljson downloads the last 30 days of Paypal transactions (requires a free developer account & API key).

paypaljson2csv

paypaljson2csv (python) converts paypaljson's output to CSV, with format similar to Paypal's manually-downloaded CSV.

HLEDGER-RUNNING

These scripts run hledger via its CLI, eg to help you produce a particular report without needing to remember a complicated command line. They might also consume its text or CSV or JSON output. They can be small shell aliases or functions (typically defined in shell startup files like ~/.bashrc) or individual script files written in shell or another language (typically kept in ~/bin/ or elsewhere in $PATH).

bashrc

bashrc contains many example bash aliases and functions. After installing the bin scripts: as a bash user,

# customise FINDIR and LEDGER_FILE at the top of bin/bashrc
$ . bin/bashrc
$ fin        # list the scripts available

ft

ft is a way to organise your finance-related reports and scripts using standard bash. (See also justfile below.)

$ alias f=~/src/hledger/bin/ft
$ f
--------------------------------------------------------------------------------"; }
ft - finance tool: run financial reports and finance-related scripts
Usage: ft [COMMAND [ARGS]]
Commands:
help           show this help
get-csv        download auto-downloadable CSVs (paypal)
import-dry     import new downloaded transactions to the journal, dry run
import         import new downloaded transactions to the journal, logging and not printing errors
get-prices     [PRICEHISTFETCHOPTS] - download prices for main commodities (default: today's)
bs             show balance sheet
is             show income statement
a              show assets
r              show revenues
x              show expenses
ab             show assets bar chart
rb             show revenues bar chart
xb             show expenses bar chart
al             show assets line chart
rl             show revenues line chart
xl             show expenses line chart
forecast       print transactions predicted by forecast rules from last week on
household      show a draft month-end household adjustment transaction for last month
consulting     show consulting revenue
bin            [PAT]  show all scripts in $DIR/bin/[bashrc] (default: ~/finance/)
OTHERCMD               [ARGS] run other hledger commands on the default journal

Add hledger options to customise reports.

tt

tt is a similar bash multi-script for time reports.

$ alias t=~/src/hledger/bin/tt
$ t
--------------------------------------------------------------------------------
tt - time tool: run time reports and time-related scripts
Usage: tt [COMMAND [ARGS]]
Commands:
help            show this help
dash            show time dashboard, redisplaying when timelog files change
status          show current time status
what            what happened ? Show largest balances first, today and depth 1 by default
dots            print line of N dots, grouped in 4s (suitable for timedot)
x               horizontal time summary this year, monthly by default
y               vertical time summary this year, monthly by default
rweeks          recent weeks' time budgets
weeks           this and last week's time budgets
hours           show a bar chart of daily hours
accunused       show unused / undeclared accounts
accunusedcat    show unused / undeclared accounts by category
accadd          add declarations for all undeclared accounts
budgets         show monthly time budget performance this year
budgetsy        show monthly time budget performance this year, vertically
budgetsw        show weekly time budget performance this year
budgetswx       show weekly time budget performance this year, horizontally
OTHERCMD        [ARGS] run other hledger commands on $TIMELOG

Add hledger options to customise reports.

justfile

https://github.com/casey/just is like make, but easier and more suitable for running commands. It is a nice tool for organising financial reports and scripts! More on hledger and just.

Here is a justfile reimplementing the ft and tt scripts more simply:

$ brew install just  # eg
$ alias j=just
$ cd ~/finance
$ cp ~/src/hledger/bin/justfile .  # or start from scratch: just --init
$ j
justfile commands:
    watch CMD                      # rerun the given command with watchexec whenever local files change
    get-csv                        # download auto-downloadable CSVs (paypal)
    import-dry                     # import new downloaded transactions to the main journal, dry run
    import                         # import new downloaded transactions to the journal, logging and not printing errors
    get-prices *PRICEHISTFETCHOPTS # show prices for main commodities (default: today's)
    bs *HLEDGERARGS                # show balance sheet
    is *HLEDGERARGS                # show income statement
    a *HLEDGERARGS                 # show assets
    r *HLEDGERARGS                 # show revenues
    x *HLEDGERARGS                 # show expenses
    ab *HLEDGERARGS                # show assets bar chart
    rb *HLEDGERARGS                # show revenues bar chart
    xb *HLEDGERARGS                # show expenses bar chart
    al *HLEDGERARGS                # show assets line chart
    rl *HLEDGERARGS                # show revenues line chart
    xl *HLEDGERARGS                # show expenses line chart
    forecast *HLEDGERARGS          # print transactions predicted by forecast rules from last week on
    household *HLEDGERARGS         # show a draft month-end household adjustment transaction for last month
    consulting *HLEDGERARGS        # show consulting revenue
    tdash *HLEDGERARGS             # show time dashboard, redisplaying when timelog files change
    tstatus *HLEDGERARGS           # show current time status
    twhat *HLEDGERARGS             # what happened ? Show largest time balances first, today and depth 1 by default
    tdots N                        # print line of N dots, grouped in 4s (suitable for timedot)
    tx *HLEDGERARGS                # horizontal time summary this year, monthly by default
    ty *HLEDGERARGS                # vertical time summary this year, monthly by default
    tweeks *HLEDGERARGS            # this and last week's time budgets
    tweekspast *HLEDGERARGS        # recent past weeks' time budgets
    thours *HLEDGERARGS            # show a bar chart of daily hours
    taccunused *HLEDGERARGS        # show unused / undeclared time accounts
    taccunusedcat *HLEDGERARGS     # show unused / undeclared time accounts by category
    taccadd *HLEDGERARGS           # add declarations for all undeclared time accounts
    tbudgets *HLEDGERARGS          # show monthly time budget performance this year
    tbudgetsy *HLEDGERARGS         # show monthly time budget performance this year, vertically
    tbudgetsw *HLEDGERARGS         # show weekly time budget performance this year
    tbudgetswx *HLEDGERARGS        # show weekly time budget performance this year, horizontally

watchaccounts

watchaccounts shows hledger account names, updating on file change under the current directory. Arguments are passed to the hledger accounts command. Useful when cleaning up accounts.

$ watchaccounts expenses -2
$ watchaccounts -f time.journal client1 date:thismonth -l

sortandmergepostings

sortandmergepostings is an adventuresome awk script intended to clean up and merge similar postings in a transaction (see original discussion). It sorts postings so that positive ones are first, negative ones last. Within each sign, postings are sorted alphabetically by account name. Lastly if there are multiple postings to the same account in the same direction, it tries to merge them (by leaving some amounts blank). Piping the output to hledger print can recalculate the missing amounts. Multiple runs might be needed to clean up all duplicates.

$ sortandmergepostings input.journal | hledger -f - print -x

hledger-simplebal

hledger-simplebal shows how to reliably report a single machine-readable number with hledger. This and the other "hledger-" scripts are add-on commands.

$ hledger simplebal

hledger-bar

hledger-bar prints quick bar charts in the terminal.

$ hledger bar reimbursement
2023-01	++++++
2023-02	++
2023-03	++
2023-04	-------
$ hledger bar                                        # show help
$ hledger bar food                                   # monthly food expenses
$ hledger bar -- 1 --count food                      # monthly food posting counts
$ hledger bar -- type:c not:tag:clopen cur:\\\\$ -W  # weekly cashflow, $ only
$ hledger bar -- type:al not:tag:clopen cur:\\\\$    # monthly net worth change ($)
$ hledger bar -- type:rx --invert cur:\\\\$          # monthly profit/loss ($)
$ hledger bar -- -v 1 -f $TIMELOG -D                 # daily hours, with numbers

hledger-git

hledger-git provides easy version control for your journal files, using git. Run it with no arguments for help.

$ hledger git log
$ hledger git status
$ hledger git record [MSG]

hledger-pijul

hledger-pijul provides the same thing using the pijul version control system..

$ hledger pijul log
$ hledger pijul status
$ hledger pijul record [MSG]

hledger-edit

The hledger-utils python package provides a hledger-edit command to edit the queried transactions in your $EDITOR no matter what file they reside in.

Install or upgrade:

$ pip install -U hledger-utils    # might be slightly different on your system

Examples:

# Opens your $EDITOR or $VISUAL with only costs in Florida 
# (if you named and tagged them like that)
# edit the transactions, save and exit your editor, 
# then the changes are distributed to the original files
$ hledger edit Cost tag:location=Florida
# Automate changes by setting `$EDITOR` to a script
# (here all food we had on that one day in Florida was Fast Food 🌭 and we initially forgot to write that) 
EDITOR='perl -pi -e "s|Cost:Food|Cost:Food:Fast Food|g"' hledger edit tag:location=Florida date:2022-12-20

asciicast

hledger-plot

The hledger-utils python package provides a hledger-plot command for generating charts with matplotlib.

Install or upgrade:

$ pip install -U hledger-utils    # might be slightly different on your system

Examples:

$ hledger-plot -h
$ hledger plot -- bal -DH ^Assets -2

hledger-lots

hledger-lots shows a lots report, or generates a lot sale transaction, using FIFO strategy (and without needing subaccounts for lots).

Install or upgrade:

$ pip install -U hledger-lots

Examples:

$ hledger lots
$ hledger lots view
$ hledger lots list

hledger-report1.sh

hledger-report1.sh is a custom compound report done in shell. See also hledger-report1.hs.

HLEDGER-INTEGRATED

These Haskell scripts use the hledger-lib API for maximum power and robustness; they can do anything hledger's built-in commands can do.

hledger-script-example

hledger-script-example.hs is a template for writing your own hledger-integrated add-on command. It has the same structure as most of the add-ons here:

  • a stack script for robustness
  • providing command line help
  • accepting common hledger options

hledger-print-location

hledger-print-location.hs is a variant of hledger's print command that adds the file and line number to every transaction, as a tag:

$ hledger print-location -f hledger/examples/sample.journal desc:eat
2008/06/03 * eat & shop
  ; location: /Users/simon/src/hledger/examples/sample.journal:30
  expenses:food                  $1
  expenses:supplies              $1
  assets:cash

hledger-swap-dates

hledger-swap-dates.hs prints transactions with their date and date2 fields swapped.

hledger-check-tagfiles

hledger-check-tagfiles.hs interprets all tag values containing a / (forward slash) as file paths, and checks that those files exist. hledger-check-tagfiles.cabal.hs is the same command implemented as a cabal script rather than a stack script.

hledger-register-max

hledger-register-max.hs runs a register report and prints the posting with largest historical balance.

$ hledger-register-max -f examples/bcexample.hledger checking
2013-01-03 Hoogle | Payroll  Assets:US:BofA:Checking      1350.60 USD  8799.22 USD
$ hledger register-max -- -f examples/bcexample.hledger checking
2013-01-03 Hoogle | Payroll  Assets:US:BofA:Checking      1350.60 USD  8799.22 USD

hledger-check-postable

hledger-check-postable.hs check that no postings are made to accounts declared with a postable:n or postable:no tag. This can be used as a workaround when you must declare a parent account to control display order, but you don't want to allow postings to it. Eg, to allow postings to assets:cash but not assets (remember that account tags are inherited):

account assets         ; postable:n
account assets:cash    ; postable:

hledger-check-fancyassertions

hledger-check-fancyassertions.hs checks account balances over time in more complex ways than hledger's built-in balance assertions.

hledger-combine-balances

hledger-combine-balances.hs shows balance reports for two different periods side by side.

hledger-balance-as-budget

hledger-balance-as-budget.hs uses one balance report to set budget goals for another balance report.

hledger-smooth

hledger-smooth.hs is an incomplete attempt at automatically splitting infrequent/irregular transactions.

hledger-move

hledger-move.hs helps make subaccount/cost-preserving transfers.

hledger-report1.hs

hledger-report1.hs is a custom compound report done in haskell. See also hledger-report1.sh.

hledger-txnsbycat.hs

hledger-txnsbycat.hs is a mixture of a balance report and a register report; it shows each account's transactions under the account's balance.

HOW TO

Install scripts

To use these bin scripts you must ensure they are in your $PATH and runnable:

  • Shell scripts: you may need bash, or to adapt the scripts for your shell.
  • Python scripts: you'll need python 3 and pip.
  • Haskell scripts: you'll need stack (https://www.haskell.org/get-started). Or if you know how, you can make them cabal scripts, or install their dependencies manually and use runghc/ghc.

Here's a suggested install procedure:

# Go to wherever you keep financial files:
$ cd ~/finance

# Get the hledger repo
# the fast way, without version control:
$ curl -LOJ https://github.com/simonmichael/hledger/archive/refs/heads/master.zip && unzip hledger-master.zip && mv hledger-master hledger
# or the slow way, with version control for easy diffing/updating/contributing
# git clone https://github.com/simonmichael/hledger

# Make a more convenient symlink to the bin directory:
$ ln -s hledger/bin

# Add the bin directory to your PATH. Eg as a bash user:
$ echo "export PATH=$PATH:$PWD/bin" >>~/.bash_profile"
$ export PATH=$PATH:$PWD/bin

# Optionally, compile all haskell scripts for faster startup:
$ cd hledger; bin/compile.sh

# Optionally, install the python scripts:
$ pip install -U hledger-utils
$ pip install -U hledger-lots

# Check that hledger's command list now includes the bin scripts.
# Eg "check-fancyassertions" and "swap-dates" should be listed:
$ hledger


Create a new script

To create a new hledger-integrated script, copy hledger-script-example.hs. On unix, the new script should be marked executable. This should do it:

$ cd bin
$ cp hledger-script-example.hs hledger-cmd.hs   # replace cmd with your command name
# edit hledger-cmd.hs, updating at least the command name and help
$ stack install safe text     # ensure the script's dependencies are installed
$ hledger-cmd.hs --help
cmd [OPTIONS]
  My new cmd command.
  ...
$ stack ghc hledger-cmd.hs  # optionally compile for faster startup/durability
$ hledger cmd -- --help
cmd [OPTIONS]
  My new cmd command.
  ...

Run ghcid on a script

$ stack exec --package 'safe text' -- ghcid hledger-cmd.hs 
...
Ok, one module loaded.
All good (1 module, at 10:50:48)

Run ghci on a script

$ stack ghci --package 'safe text' hledger-cmd.hs 
...
Ok, one module loaded.
...
ghci> 

Learn more about scripting hledger

See Scripting hledger.

Scripting hledger

As a command line program, with plain text data formats, hledger is easy to extend and customise with scripts and add-on commands. This page describes some different kinds of scripting you can do around hledger.

For an overview of ready-made scripts you can install, see Scripts.

Scripts are programs which call other programs to get things done. They are often (though not always):

  • small
  • ready to run immediately with no compilation step
  • quick to write and modify
  • written by you to make your particular tasks easier.

They could be as simple a shell alias remembering the options for a common report, or a small program automating a more complex operation such as invoicing.

Shell aliases

Shell aliases are the simplest type of script. Their most common use is to save typing, as in the examples below. (These and most shell examples in these docs are for the popular Bash shell; translate appropriately if you use a different shell.)

# ~/finance/bashrc
alias acc='hledger accounts'
alias act='hledger activity'
alias add='hledger add'
alias areg='hledger aregister'
alias bal='hledger balance'
alias close='hledger close'
alias print='hledger print'
alias reg='hledger register'
alias stats='hledger stats'
alias tags='hledger tags'
alias bs='hledger bs'
alias bse='hledger bse'
alias is='hledger is'
alias cf='hledger cashflow'
alias 2020='hledger -f ~/finance/2020.journal'
alias 2019='hledger -f ~/finance/2019.journal'
alias 2018='hledger -f ~/finance/2018.journal'
# ...
alias 2y='hledger -f ~/finance/2year.journal'
alias 3y='hledger -f ~/finance/3year.journal'
alias 4y='hledger -f ~/finance/4year.journal'
alias 5y='hledger -f ~/finance/5year.journal'
alias all='hledger -f ~/finance/all.journal'

or to customise hledger's commands:

alias iadd='hledger-iadd --date-format %Y/%m/%d'
alias ui='hledger-ui --watch'
alias uir='hledger-ui --watch --register'

or to remember useful reports:

alias allone="all --alias '/^([^:]+)(:personal|:business)\b/ = \1' --alias '/^revenues:consulting/=revenues' --alias '/\bwells fargo\b/=wf'"
alias budget='hledger bal --budget'
alias forecast='hledger reg -H date:today- --forecast'
alias tripactivity='hledger act tag:trip -W'

Shell functions

When aliases are too limiting, you can use functions instead:

alias bassets='bal -H ^assets:business'
alias bdebts='bal -H ^liabilities:business'
alias brevenues='bal ^revenues:business'
alias bexpenses='bal ^expenses:business'

function bsnapshot () { # business balance sheet + year-to-date income statement
 echo Business financial snapshot `date` #+%Y/%m/%d
 echo
 echo Assets today:
 bassets --flat --drop 2 --depth 4 $@
 echo
 echo Debts today:
 bdebts --flat --drop 2 --depth 4 $@
 echo
 echo Revenues this year:
 brevenues --flat --drop 2 --depth 3 $@
 echo
 echo Expenses this year:
 bexpenses --flat --drop 2 --depth 3 $@
 echo
}

Shell scripts

When aliases and functions are too limiting, you can use a shell script file:

#!/bin/sh
# ~/finance/bin/reports
date
export PATH=/Users/simon/.local/bin:$PATH
export LEDGER_FILE=/Users/simon/finance/2018.journal
/usr/bin/make -s -C /Users/simon/finance reports | /usr/bin/mail -s 'reports' simon

Make

Makefiles can work well for collections of simple scripts, especially if they are local to a particular directory.

# ~/finance/Makefile

# regular reports, mailed by cron
reports: \
	is \
	time-report \
	todo \

is:
	@echo 'Monthly Income Statement'
	hledger is -M cur:.
	@echo

YEAR:=$(shell date +%Y)
TIMEJOURNAL=time-$(YEAR).journal
HLEDGER=hledger
TIME=$(HLEDGER) --alias "/\./=:" -f $(TIMEJOURNAL)

time-report:
	@echo 'Monthly Time Report'
	$(TIME) bal -MAT -1
	@echo

todo:
	@echo "TODOs in this year's journals"
	@grep TODO $(YEAR)*
	@grep TODO $(YEAR)* | wc -l
	@echo

If you are not comfortable with Make's syntax, or when scripts get more complex, dedicated or multi-function shell scripts will be less hassle.

Add-on commands

If you name scripts hledger-something and make them executable in PATH, they'll be recognised as add-on commands.

Eg here's a simple script which adds estimatedtax as a hledger command. This one is in haskell, but it could be written in any language:

#!/usr/bin/env runhaskell
-- hledger-estimatedtax.hs
-- Given a gross income amount, estimate the corresponding federal and
-- state taxes based on historical tax per gross income.  These
-- amounts can be posted to your tax liability account, to estimate
-- taxes due from this year's income so far.

import System.Environment
import Text.Printf

-- | How much of taxable income to save for federal & state estimated tax payments,
-- these are usually about right for me
fedrate       = 0.28
staterate     = 0.04

main = do
  taxableincome <- fmap (read . head) getArgs :: IO Int
  let fedtax        = round $ fromIntegral taxableincome * fedrate
      statetax      = round $ fromIntegral taxableincome * staterate
      totaltax      = fedtax + statetax
      posttaxincome = taxableincome - totaltax
  printf (
    "Taxable income:  %5d\n" ++
    "US tax:          %5d\n" ++
    "CA tax:          %5d\n" ++
    "Total tax:       %5d\n" ++
    "Post-tax income: %5d\n")
    taxableincome fedtax statetax totaltax posttaxincome

hledger-lib scripts

These are scripts written in haskell, which use the same hledger-lib and hledger APIs that hledger uses. They can be simple interpreted scripts, or can grow as large and powerful as you need, and can be optionally compiled for more speed and durability. Example scripts and more tips can be found at https://github.com/simonmichael/hledger/tree/master/bin/.

hledger-lib scripts installed as add-ons (hledger-newcmd.hs in PATH) are a good way to prototype new commands, perhaps to become builtin commands later. (Though, converting a haskell script command to a builtin command currently requires rearranging its code; we'd like to improve this.)

Haskell scripting tips

If you want others, or future-you, to be able to run your haskell scripts reliably, make them stack scripts or cabal scripts. stack scripts are usually best for reliability, performance, and sharing with others, because:

  • they require only the stack executable to be installed on your system
  • they ensure the required GHC version is installed, installing it automatically if needed
  • they ensure the required haskell packages are installed, installing them automatically if needed
  • they specify all package versions and use known-compatible packages for more reliable builds
  • they automatically recompile themselves when changed, only when needed.

Plugins

In general we would like for hledger users to be able to quickly customise its behaviour and features with small code "plugins", without having to change and recompile hledger itself. Haskell is not a very "dynamically pluggable" language, so currently the best way to build "plugins" is with pre-processing scripts, post-processing scripts, or hledger-lib-using haskell scripts.

We identify several desirable plugin types:

Plugin typePurpose
readerParse/ingest from some data format/source to hledger journal data.
processorProcess/transform journal data, after parsing (ideally) and before reporting.
reportCalculate some kind of report data from processed journal data.
rendererRender report data in some output format.
commandProvide new CLI subcommands implementing new reports/formats/actions.
writerWrite/export complete journal data to some data format/destination.

Here are examples of each type of plugin, and the best approach for making your own. (More examples can be found at https://hledger.org/scripts.html or https://plaintextaccounting.org/#data-importconversion.)

reader

  • the builtin journal, csv, timeclock, timedot readers
  • beancount2ledger, pricehist, reckon, qb2ledger, ynab-to-ledger

A new reader can be made by generating journal or csv format for hledger to read, or with a hledger-lib script that builds hledger's journal data structure.

processor

  • the builtin amount/cost/equity inferring; amount style normalisation; auto postings; periodic transactions; default/strict checks..
  • hledger-print-location, hledger-swap-dates

Custom processing of hledger's journal data structure, after parsing, is currently not well supported; it is somewhat possible in a hledger-lib script. As an alternative, you can transform the journal or csv textually before hledger reads it.

report

  • the builtin reports: PostingsReport, AccountTransactionsReport, MultiBalanceReport, BudgetReport..
  • hledger-combine-balances
  • multi-report tools: hledger-plot, hledger-vega, hreports, r-ledger

A report also needs a command to invoke it and a renderer to show it. An add-on script can provide all three of these.

renderer

  • the report commands' builtin output formats (usually txt, csv, json,.md..)

This can be done with an add-on script, or by transforming the report's txt/csv/json/html output.

command

  • hledger-ui, hledger-web, hledger-iadd, hledger-git, hledger-move, hledger-edit, hledger-check-fancyassertions

A new subcommand can be invoked by hledger COMMAND ... and will appear in the hledger commands list. It can be made with an add-on script or program (hledger-foo in PATH).

writer

  • the print command's builtin output formats (txt, csv, json, sql)
  • ledger2beancount

A new writer can be made as a hledger-lib script, or (losing the original journal's directives and top-level comments) by transforming print's txt/csv/json/sql output.

Simon's old setup

Some notes on my setup circa 2019 and later.

  • Author: Simon Michael
  • Tested with: mac mojave, hledger 1.14

Tools

hledger, hledger-ui, ledger-mode, GNU make, ...

Files

$ tree ~/notes
/Users/simon/notes
├── 2019.journal
├── 2019.prices
├── all.journal
├── current.journal -> 2019.journal
├── forecast.journal
...

Files are in ~/notes. There's one YEAR.journal file per year containing all transactions in date order. It includes:

  • YEAR.prices containing P records for the year
  • forecast.journal containing periodic transaction rules

all.journal includes all the year journals. It provides all historical data, but is slow, and my old journals are inconsistent/broken, so it's currently rarely used.

current.journal is a symlink for scripts which don't know the year. Symbolic links are a mixed blessing, causing file path confusion in emacs, eg.

Scripts

I have convenience aliases for hledger commands and reports in ~/notes/bashrc, which is automatically sourced by my bash profile.

Data fetching and report scripts are defined in ~/notes/Makefile.

Increasingly, I am moving reports and scripts in a (more powerful and robust) Shake file, ~/notes/do.hs (details).

Version control

Journal file, included files, makefile and scripts in git. A mixture of manual and cron-based automatic committing.

Environment

The LEDGER_FILE environment variable is set to /Users/simon/notes/CURRENTYEAR.journal. This is done in some super secret way that I'll have to track down, or more likely several ways, to ensure that it is consistent for:

  • programs started from command line in iTerm/Terminal windows
  • programs started from emacs shell buffers
  • emacs modes such as ledger-mode
  • for both text-mode and graphical emacs, whether started from command line or mac GUI (Dock/Finder/Spotlight)
  • etc.

Accounts

I track both sole proprietor business accounts, with JS: prefix, and personal accounts, with sm: prefix, in one journal for convenience. These declarations ensure hledger knows the account types:

account JS:assets               ; type:A
account JS:liabilities          ; type:L
account JS:equity               ; type:E
account JS:revenues             ; type:R
account JS:expenses             ; type:X

account sm:assets               ; type:A
account sm:liabilities          ; type:L
account sm:equity               ; type:E
account sm:revenues             ; type:R, taxable personal revenues
account sm:income               ; type:R, already-taxed "salary" from JS, non-taxable income
account sm:expenses             ; type:X

Getting data

Most journal entries are generated from downloaded CSV:

  • I fetch OFX from one bank with ledger-autosync (discontinued due to high fees)

  • Transactions from three banks are aggregated and cleaned in a Google sheet by Tiller ($5/mo). I download this as CSV with a gsheet-csv script (via cron ?). (discontinued for security)

  • Transactions are downloaded from banks by Plaid (free developer account). (discontinued for security)

  • Paypal CSV is downloaded manually, then moved into place by a make rule. I use Paypal's CSV because Tiller doesn't handle multiple currencies and Paypal's extra metadata fields.

  • I download CSV manually from each bank, weeklyish.

  • I fetch CSV from Paypal's API using paypaljson and paypaljson2csv scripts.

CSV rules are never finished; they get one or two small tweaks most times I import. They don't cover 100% of my possible entries; a few that are too hard to generate just get generated partially, with a '; TODO:` tag for me to fix them up manually. There aren't many of those at this point

I fetch currency prices with barrucadu's market-prices.py script (details).

Entering data (2021)

For transactions downloaded from banks:

  • In an iterm3 tab titled FINANCE, I have a TUI emacs with two side-by-side windows.
  • In the first window is 2021.journal, with ledger-mode and auto-revert-mode enabled.
  • In the second window I switch to a shell and run make Import to download and import latest transactions.
  • These show up in the journal right away. I switch focus there, select all the new entries and hit M-q to realign them with ledger-mode (which is better at that).
  • I process them one at a time from the top, marking each one cleared (C-c C-e) when it looks good.
  • When there's an unknown posting, I:
    • replace the unknown account with the appropriate account (assuming I know it)
    • switch to common.rules in the second window, and search for that account or its parent (assuming it's in the rules somewhere)
    • add some portion of the new entry's description as a new pattern for this account. Or tweak the existing patterns so it will be matched next time (or to avoid over-matching by the wrong rule).
  • when all new entries are marked cleared, I git commit the journal and any updated rules file(s). Or if I don't, it will be auto-committed by a nightly cron job, in theory.
  • I do this daily-ish, so it's a small number of new txns each time.

For transactions which don't appear in downloaded data (cash transactions, etc.):

  • Note it somewhere, anywhere, as quickly possible, or it will be forgotten (and I'll have to add an unexplained cash adjustment transaction later).
  • If the laptop is out of reach, that will usually be a brief note on today's page in a phone notes app (Obsidian, currently) or a paper notebook. Later that gets copied to the laptop.
  • On the laptop, my usual routine is:
    • get to the place of data entry
      • iterm3 app (or start it if not running),
      • FINANCE tab where a TUI emacs (client) is running (or create that tab/start that emacs client),
      • emacs window showing the journal (or C-j l if not showing),
      • end of buffer (M->).
    • add the journal entry: either
      • if short of time: just a commented line, or date and description (and expand it later)
      • if a familiar transaction: search back for a similar one, copy, jump to the end again, paste, adjust as needed (especially the date)
      • otherwise type it in, starting with the date, using ledger-mode's completion (TAB), or hippie-expand's completion (M-/) if they help. (ledger-mode required careful configuration for best behaviour with account names, the main thing I recall is turn on ledger-complete-in-steps.)
      • when the journal entry is finalised, mark it cleared (C-c C-e).

Workflow (2023)

I run make Import (equivalent to: make csv which fetches latest bank & paypal csvs, plus make import which essentially runs hledger import *.csv).

Then I review in emacs+ledger-mode+flycheck-mode. All the imported transactions are uncleared, showing up red. Flycheck also highlights problems like "expenses:unknown" or failing assertions or out of order dates. I clear each one, fixing (and maybe tweaking CSV rules) where needed.

The recentassertions check may force me to add a transaction with newer balance assertions for the main accounts; I describe that "reconcile", I fill in the balances hledger expects, and at the same time I check they match the real-world balances.

Finally when all the red is gone and all checks are passing - enforced by adding this command to .git/hooks/precommit:

#!/bin/sh
...
# check main hledger journal
hledger check -s ordereddates recentassertions

...I commit the changes to git.

I have a nightly cron job which would do this automatically, but by habit and for clean commits I usually do them manually, with separate "txns", "rules" and "scripts" commits. (If you try using cron like this, note failing checks can reveal account names and balances in the error message and cron might email those in plain text over the internet.)

For troubleshooting: when downloading a CSV the previous copy is saved as FILE.csv.old.

Cash transactions are entered in emacs, using ledger-mode. Mostly by copying and pasting similar past transactions.

When rewriting account names, I use either ledger-mode completion (TAB) or dabbrev-expand completion (M-/), which have different strengths.

SM's time & task dashboard

SM has invented a cool time and task-tracking aid. As you work on a mac computer (A), you wonder how long you have been here and what you should be doing instead. So you press Cmd-Shift-Return to call up your Time and Task Dashboard (B). The HotKey app (C) recognises your keypress and opens Terminal (D), which previously you positioned at top half of screen by pressing Ctrl-Opt-Cmd-Up to trigger a Hammerspoon window layout script (E). Inside the Terminal (D) is a text-mode Emacs (F) which you have split horizontally with Ctrl-x 3 into four side-by-side windows (G, H, I and J). In the first window (G), a vterm (K) is running a time script (L), which is defined in a justfile (M) and uses watchexec (N), cron (O), hledger (P) and various unix tools (Q) to show an updating time report (R). You review this to see where you have spent time today, your time goals if any, and how long it has been since you last updated the time log and since the computer woke up. Armed with this information you make any needed updates to your timedot-format time log (S), which is in the second window (H), usually narrowed to the current day. If you need to run reports or queries, you use the third window (I) which contains an emacs shell prompt (T). You have also been keeping more detailed notes and todo lists in a weekly notes file (U), named like 2023W51.md, with Obsidian (V). To see today's goals and progress summarised, you glance at the fourth window (J), where a second vterm is running a todo script (W), also defined in justfile (M), showing an updating tasks report (X). This displays the list items from today's section of the weekly notes file (U) with empty checkboxes in red, filled checkboxes as green check marks, and non-checkbox bullets as black squares. You review today's green completed tasks, red uncompleted tasks and black unplanned activities. Reoriented and up to date, you press Cmd-Shift-Return again to hide the Terminal (D), and proceed with clarity and confidence.


time (L):


# Defined in ~/.bash_profile.local: $TIMELOG, $TIMEDATA

TIMEDIR     := `dirname "$TIMELOG"`
TIMELOGALL  := TIMEDIR / 'time-all.journal'

# This redisplays only when a file listed by `hledger -f $TIMELOG files` is modified.
# To force a per minute display as well, have $TIMELOG include a dummy file (.tick)
# and configure a cron job to touch that every minute.
# (This is better than touching the timelog file itself, which confuses editors.)
#

# show time status, redisplaying when timelog files change
time *ARGS:
    #!/usr/bin/env bash
    set -euo pipefail
    cd "$TIMEDIR"
    opts=  #--poll=10  # <- uncomment to fix symlinked files being ignored
    $WATCHEXEC $opts --no-vcs-ignore \
      --filter-file=<(hledger -f "$TIMELOG" files | sed -E "s|$TIMEDIR/||g") \
      -c -r just time1 "$@"

# show time status, redisplaying every minute with watch
# dash-1m *ARGS:
#     watch -n60 -c tt status
# }

# show current time status
time1 *ARGS:
    #!/usr/bin/env bash
    # XXX changing the above to osh causes tdash to hang
    set -euo pipefail
    date=$(if [ "$(builtin type -p gdate)" ]; then echo gdate; else echo date; fi)
    stat=$(if [ "$(builtin type -p gstat)" ]; then echo gstat; else echo stat; fi)
    curtime=$($date +'%H:%M %Z, %a %b %-e %Y')
    modtime=$($date +'%H:%M %Z' -r "$TIMEDATA")
    modsecs=$($stat -c %Y "$TIMEDATA")
    nowsecs=$($date +%s)
    agesecs=$((nowsecs - modsecs))
    agemins=$(python3 -c "print($agesecs/60)")
    agehrs=$(python3 -c "print($agesecs/3600.0)")
    ageqtrhrs=$(python3 -c "print(round($agesecs/900.0))")
    agedots=$(just tdots "$ageqtrhrs")
    printf "Current time:  %s\n" "$curtime"
    # old, for osh: use env here to run the system printf, which supports floating point
    env printf "Timelog saved: %s, %.0fm / %.1fh / %s ago\n" "$modtime" "$agemins" "$agehrs" "$agedots"
    # Show the current day/week/month budget status.
    printf "Time plans:\n"

    # calculate each period's budget from daily budget
    hledger -f "$TIMELOG" bal -1 -p 'daily today'        --budget=Daily -E "$@" | tail +2
    # hledger -f "$TIMELOG" bal -1 -p 'weekly this week'   --budget=Daily -E "$@" | tail +2
    # hledger -f "$TIMELOG" bal -1 -p 'monthly this month' --budget=Daily -E "$@" | tail +2

    # or use each period's specific budget
    # hledger -f "$TIMELOG" bal -p 'daily today'        --budget=Daily   -1 | tail +2
    # hledger -f "$TIMELOG" bal -p 'weekly this week'   --budget=Weekly  -1 | tail +2
    # hledger -f "$TIMELOG" bal -p 'monthly this month' --budget=Monthly -1 | tail +2

    echo
    hledger -f "$TIMELOG" check -s tags ordereddates || true
    # this comes last because it's slow and variable length
    echo
    printf "Display activity:\n"
    wakelog today | tail -n 6


todo (W):

# keep these synced with obsidian:

WEEKNOTES := '~/notes/CLOUD/`date +%YW%V`.md'
TODAYHEADING := '^## ' + `date +'%a %-d'`

# print the path of this week's notes file
@weeknotes:
    echo {{ WEEKNOTES }}

# watch today's tasks, with any rg options (like -c)
@todo *OPTS:
    if [[ ! -f {{ WEEKNOTES }} ]]; then echo "{{ WEEKNOTES }} does not exist yet"; exit 1; fi
    $WATCHEXEC -w {{ WEEKNOTES }} -- 'clear; just todo1 {{ OPTS }}'

# list tasks in today's notes (checkboxes and other bullet items)
@todo1 *OPTS:
    if [[ ! -f {{ WEEKNOTES }} ]]; then echo "{{ WEEKNOTES }} does not exist yet"; exit 1; fi
    awk '/^#+ Inbox/{f=0};f;/{{ TODAYHEADING }}/{f=1}' {{ WEEKNOTES }} \
    | sed 's/\[x]/✅/' \
    | gsed -E 's/- (\w)/- ◼️  \1/' \
    | rg {{ OPTS }} '^ *\- (\[[ x]\])?' \

# list tasks in this week's notes
@todo-week *OPTS:
    rg {{ OPTS }} '^ *\- (\[[ x]\])?' {{ WEEKNOTES }}


wakelog:

#!/usr/bin/env bash
# Show notable mac sleep/wake events, trimmed to terminal width
# for tidy display, eg by watch. 
# Requires: cut grep sed tput
# XXX slow

usage() {
    cat <<EOF
Show notable mac sleep/wake events, trimmed to terminal width.
Commands: 
wakelog today     - show main sleep/wake events so far today
wakelog yesterday - show main sleep/wake events yesterday
wakelog list      - show main recent sleep/wake events
wakelog all       - show all recent sleep/wake events

EOF
# ^ keep synced with functions below
}

# XXX in some cases this might not work, then the log lines are not cut,
# and their long trailing whitespace can cause lines to wrap.
width=$(tput cols)

grep="grep -E"
grep="rg --color=never"

today() {
    list | $grep "$(date +^%Y-%m-%d)"; true
}

yesterday() {
    list | $grep "$(date -v-1d +^%Y-%m-%d)"; true
}

listv() { # list simplified sleep/wake/display off/on events from mac power event log
    pmset -g log | $grep '((Sleep|Wake)  +|Display is)' \
    | sed -E \
      -e "s/\t/ /g" \
      -e 's/(Notification|Sleep|Wake) *	//' \
      -e 's/is turned //' \
      -e 's/Entering Sleep state/Sleep/' \
    | $grep -v '(DarkDarkWake|Maintenance Sleep|Back to Sleep)' \
    | cut -c 1-"$width"  # clip long lines
}

list() { # list terse display off/on events from mac power event log
    pmset -g log | rg '^(...................).*Display is turned (.*)' -or'$1 $2' \
    | sed -E \
      -e "s/\t/ /g" \
    | cut -c 1-"$width"
}

all() {
    pmset -g log \
    | $grep -e " Sleep " -e " Wake " -e " DarkWake " -e " Wake Requests " \
    | sed -E -e "s/\t/ /g" \
    | cut -c 1-"$width"
}

if declare -f "$1" > /dev/null; then "$@"; else usage; fi

Sponsoring hledger

Building and supporting good software and documentation is very costly; hledger comes from many thousands of skilled person-hours, sustained over 15+ years. Your support is invaluable and greatly appreciated! Is it the right time for you to help ? Consider:

  • Has this project been helpful to you or your organisation ?

  • Have you achieved enough financial success to be able to donate a little (or a lot) ?

  • Would you like to cultivate the psychological and spiritual benefits of giving back / paying forward ?

  • Would you like hledger to be around for a long time ? To remain actively supported ? To improve faster ?

  • Would you like to support our core mission ? Which is:

    To help more people achieve financial literacy, discipline and freedom, and to help grow a shared global culture of accountability and sustainability.
    (See also: FAQ)

How to sponsor

It's easy, even if not yet as efficient as we'd like. Neither hledger project nor our fiscal host is a registered charity, so your donations may not be tax-deductible, and the CFO (Simon) pays US and state income tax on all donations, in addition to the fees below.

Our fiscal host on Open Collective is The Open Source Collective, "a non-profit umbrella organisation providing financial and legal infrastructure for thousands of open source projects". So their fee is at least a sort of donation to support free/open-source software.

These donations are private and help support Simon. You can also support by offering me bounties or consulting gigs.

  • github (Fees: 0%)
  • liberapay (Fees: ~3%)
  • paypal (Fees: ~3%)
  • You can find their names at CREDITS, check their website/Github profile, and offer donations, bounties or paid work.

These donations are public and reported with hledger. They are deployed by the CFO (Simon), with input from the hledger team and community. So far, project funds have been used for project expenses, regression bounties, and one social good donation.

  • liberapay (Fees: ~3%)
  • opencollective (Fees: ~3%/$0.59 payment processor + 10% fiscal host)
  • issue bounty (simplest)
    • post a bounty pledge on an open issue
    • when resolved, pay the claimant directly (honour system).
  • bountysource bounty
    • search for the issue you want to sponsor
    • add some bounty to it
    • post a comment on the issue announcing the bounty
    • when resolved, pay through Bountysource.
  • opencollective bounty

Sponsors

We thank you, our generous sponsors, for your support, including:

Tags tutorial

Author: Robert Nielsen (YouTube: hledger fan) with invaluable input from Simon Michael

Don’t be scared away from using tags in your hledger accounting this Halloween, or any other time for that matter. This is a tutorial on using tags with hledger, so if the idea of using tags has been haunting you, but you are not really sure how to use them, read on.

Let’s start with a file showing someone’s hledger accounting around the time of Halloween:


2022/09/25 ACME Costume
   Expenses:Entertainment     $45.99 ; Amoeba Man
   Liabilities:CreditCard

2022/10/30 Cheap-O-Gas
   Expenses:Automotive     $37.15
   Liabilities:CreditCard 

2022/11/01 Medical Associates
   Expenses:Medical  $80.00 ; upset stomach
   Liabilities:CreditCard

2022/12/15 West End Dentistry
   Expenses:Dental  $160.00 ; two fillings
   Liabilities:CreditCard


We want to categorize three of the above transactions (costume, doctor visit after overeating candy, and fillings as a result of too consuming too many sugary Halloween treats) as expenses attributable to Halloween. One option would be to create a new category of expenses for Halloween, but what if we want to also keep the other categories, such as Entertainment, Medical, and Dental? For example, we want to have the visit to the dentist categorized as a dental expense, but at the same time we should be able to find it under expenses related to Halloween. One solution is to add a tag for Halloween.

Mechanics of Writing a Tag

One of the simplest tags is a word, which is inside a comment, immediately followed by a colon. In the file below, you will see that three of the transactions have a tag, named Halloween, added to them:


2022/09/25 ACME Costume ; Halloween:
   Expenses:Entertainment     $45.99 ; Amoeba Man
   Liabilities:CreditCard

2022/10/30 Cheap-O-Gas 
   Expenses:Automotive     $37.15
   Liabilities:CreditCard 

2022/11/01 Medical Associates ; Halloween:
   Expenses:Medical  $80.00 ; upset stomach
   Liabilities:CreditCard

2022/12/15 West End Dentistry ; Halloween:
   Expenses:Dental  $160.00 ; two fillings
   Liabilities:CreditCard
 

Using Tags

Once we incorporate tags in our hledger file, we can use them in a variety of ways. Here, we will look at one way, which is to use the tags to filter a command.

This command:

$ hledger register Expenses tag:Halloween

Produces something like the following output (i.e., all the Expenses tagged “Halloween”):

2016/09/25     ACME Costume           Liabilities:CreditCard   -$45.99          -$45.99
2016/11/01     Medical Associates     Liabilities:CreditCard   -$80.00          -$125.99
2016/12/15     West End Dentistry     Liabilities:CreditCard  -$160.00          -$285.99

Notice that the automotive expense for gasoline does not appear in the above output. This is because the transaction was not tagged as “Halloween.” Thus, you see how you can narrow the scope of a command such as register, so that the command applies to just those transactions with a specific tag.

And because we chose to use a tag, as opposed to creating a new expense category, we can still find out what we spent on dentistry, doctor’s bills, and so on.

Adding Values to Tags

Just being able to create a simple tag is frighteningly useful. However, you can also add values to tags, so that you can narrow a search to a tag value. This is a real treat to learn how to do!

For this example, we will be using expenses of the mummy, a monster popular in fiction and film. Our mummy in question likes to have a variety of changes of wrappings, and so it has made several purchases from its favorite online store:

2022/05/13 AcmeWrappings.com
    Expenses:Clothing                         $28.00
    Liabilities:MonsterCard

2022/05/15 AcmeWrappings.com
    Expenses:Clothing                         $44.90  
    Liabilities:MonsterCard

2022/05/17 AcmeWrappings.com
    Expenses:Clothing                         $36.50  
    Liabilities:MonsterCard

2022/05/20 AcmeWrappings.com
    Expenses:Clothing                         $58.99  
    Liabilities:MonsterCard

2022/05/28 AcmeWrappings.com
    Expenses:Clothing                         $39.00  
    Liabilities:MonsterCard

The Mechanics

The mummy would like to note the color of each wrapping it purchases. Therefore, it creates a tag “color” and adds one or more words telling the color. For example, the transaction below has the tag “color” and the value is “ancient white”:

2022/05/15 AcmeWrappings.com  
    Expenses:Clothing  $44.90  ; color:ancient white
    Liabilities:MonsterCard

Note that you can optionally put a space after the colon, as shown immediately below:

2022/05/15 AcmeWrappings.com  
    Expenses:Clothing  $44.90  ; color: ancient white
    Liabilities:MonsterCard

Here is the file that we will be working with:

2022/05/13 AcmeWrappings.com
    Expenses:Clothing  $28.00  ; color:parchment
    Liabilities:MonsterCard

2022/05/15 AcmeWrappings.com
    Expenses:Clothing  $44.90  ; color:ancient white
    Liabilities:MonsterCard

2022/05/17 AcmeWrappings.com
    Expenses:Clothing  $36.50  ; color:dust
    Liabilities:MonsterCard

2022/05/20 AcmeWrappings.com
    Expenses:Clothing  $58.99  ; color:ancient white
    Liabilities:MonsterCard

2022/05/28 AcmeWrappings.com
    Expenses:Clothing  $39.00  ; color:parchment
    Liabilities:MonsterCard

In the above file, we have added values for the color of each of the wrappings that were purchased, specifically,

dust
parchment
ancient white

Let’s say we want to list all the wrappings that were dust colored.

We could use the command:

$ hledger register tag:color=dust

The above would output something like the following (i.e., the one expense that was tagged with the color dust:

2022-05-17 AcmeWrappings.com Expenses:Clothing $36.50 $36.50

On the other hand, if we wanted to list only the wrappings that were parchment color, we could use the command:

$ hledger register tag:color=parchment

The above command, lists the following:

2022-05-13 AcmeWrappings.com   Expenses:Clothing  $28.00 $28.00
2022-05-28 AcmeWrappings.com  Expenses:Clothing   $39.00 $67.00

Watch Out for the Spaces

Now, let’s look for every wrapping colored “ancient white”. If we try:

$ hledger register tag:color=ancient white

We get exactly nothing. What happened? As you most likely guessed from the title of this section, the space between “ancient” and “white” is the cause of the problem. To fix this, we can put the text in quotation marks.

Therefore, you will need something like the following, to find all the wrappings colored “ancient white”:

$ hledger register tag:color="ancient white"

Combining Tags and Comments

What if you want both a tag and a comment in the same line? A tag goes in a comment, but what are the restrictions? Glad you asked that question!

Answer: hledger tags go inside a comment, and there are two options. First, the tag can go at the end of the comment.

For example, you make a purchase of an inflatable pumpkin and want to include that fact in a comment. However, in the same line you also want to tag it as holiday:Halloween. One option is to write a comment followed by a tag:

2022/09/26 ACME Holiday Supplies 
  Expenses:Entertainment    $58.99 ; inflatable pumpkin holiday:Halloween
  Liabilities:CreditCard

The second option is to end the tag with a comma. You can then put a comment after the comma.

For example, we have the tag first (holiday:Halloween) followed by a comma. After the comma is a comment (inflatable pumpkin).

2022/09/26 ACME Holiday Supplies 
  Expenses:Entertainment    $58.99 ; holiday:Halloween, inflatable pumpkin
  Liabilities:CreditCard

Note that if you put a comment after the tag without separating the tag and comment with a comma, the comment becomes part of the tag value, and this can cause unwanted results. You do not want unwanted results when working with important data!

Finally, it's possible to have a comment, followed by a tag, and as long as you end the tag in a comma, you can have additional comments after the comma at the end of your tag.

Multiple Tags

Our friend the mummy, introduced earlier, is very happy being able to track the color of its wrappings. In addition, however, it would also like to track the type of fabric and the width of the cloth. How does it do this?

Fortunately, hledger allows multiple tags, with each tag separated by a comma. Here is an example:

; fabric:cotton, width:15, color:parchment

In addition to recording the above, our mummy wants to track which type of wrappings it buys. Specifically, it needs to track the type of fabric, the width of the cloth, and the color of the material. How does it do this?

Fortunately, hledger allows multiple tags, with each tag separated by a comma. Here is an example:

; fabric:cotton, width:15, color:parchment

The mummy now adds the tags to each purchase, resulting in:

2022/05/13 AcmeWrappings.com
    Expenses:Clothing  $28.00  ; fabric:cotton, width:15, color:parchment
    Liabilities:MonsterCard

2022/05/15 AcmeWrappings.com
    Expenses:Clothing  $44.90  ; fabric:nylon, width:20, color:ancient white
    Liabilities:MonsterCard

2022/05/17 AcmeWrappings.com
    Expenses:Clothing  $36.50  ; fabric:wool, width:15, color:dust
    Liabilities:MonsterCard

2022/05/20 AcmeWrappings.com
    Expenses:Clothing  $58.99  ; fabric:wool, width:20, color:ancient white
    Liabilities:MonsterCard

2022/05/28 AcmeWrappings.com
    Expenses:Clothing  $39.00  ; fabric:cotton, width:30, color:parchment
    Liabilities:MonsterCard

To see everything purchased made of wool, the mummy types:

$ hledger register tag:fabric=wool

The result is:

2022/05/17 AcmeWrappings.com  Expenses:Clothing  $36.50  $36.50
2022/05/20 AcmeWrappings.com  Expenses:Clothing  $58.99  $95.49

Or to see which wrappings had a width of 20:

$ hledger register tag:width=20

which outputs...

2022/05/15 AcmeWrappings.com  Expenses:Clothing  $44.90   $44.90
2022/05/20 AcmeWrappings.com  Expenses:Clothing  $58.99  $103.89

Multiple Tags in a Query

Multiple tags can be used in the same query. For example, our monster friend wants to find out how much parchment colored cotton it has purchased:

$ hledger register tag:fabric=cotton tag:color=parchment

Notice that there is no comma between the two tags in the above query. You use the comma to separate tags in the data file, but not the query.

By the way, the above command outputs:

2022/05/13 AcmeWrappings.com   Expenses:Clothing  $28.00  $28.00
2022/05/28 AcmeWrappings.com   Expenses:Clothing  $39.00  $67.00

Each wrapping purchased above is both made of cotton AND has the color of parchment.

If you wish to find all wrappings, say, made of wool OR colored parchment, you can run two queries. First, we find all the wool wrappings:

$ hledger register tag:fabric=wool

Then we find all the parchment colored ones:

$ hledger register tag:color=parchment

Expenses per Category

One common question is how much did we spend in total for each tag value? For example, how much did the mummy spend on each color of wrapping? To find out, we can run a balance report for our expenses pivoted by color:

$ hledger balance expenses --pivot color

The result is:

             $103.89  ancient white
              $36.50  dust
              $67.00  parchment
--------------------
             $207.39

Aha, we see a lot of spending on “ancient white”.

Similarly, if we want to see the expenses by type of fabric, we type:

$ hledger balance expenses --pivot fabric

And we get:

              $67.00  cotton
              $44.90  nylon
              $95.49  wool
         --------------------
             $207.39

From the above, we observe that the mummy has spent the more on wool wrappings than on either cotton or nylon ones.

Summary

This tutorial has shown how to:

  •  Add a tag to an hledger transaction
    
  •  Use tags with the register command to list only those expenses with a given tag
    
  •  Add values to tags
    
  •  Use tag values with the register command to list only those expenses a given tag with a given value
    
  •  Combine comments and tags in the same line
    
  •  Use multiple tags in the same line
    
  •  Use the --pivot option to total expenses by tag value
    

Hledger Tag Summary with Examples

Add a tag to an hledger transaction

Inside a comment write a word immediately followed by a colon:

2022/09/25 ACME Costume ; Halloween:
   Expenses:Entertainment     $45.99
   Liabilities:CreditCard

A tag can apply to a whole transaction, as in the above, or just one of the lines as in:

2022/09/25 ACME Costume
   Expenses:Entertainment     $45.99 ; Halloween:
   Liabilities:CreditCard

Use tags with the register command to list only the expenses for a given tag

$ hledger -f Halloween2.hledger register Expenses tag:Halloween

Add values to tags

To add a value to a tag, add one or more words to the tag:

2022/05/20 AcmeWrappings.com
    Expenses:Clothing  $58.99  ; color:ancient white
    Liabilities:MonsterCard

Limit a command to tags with specified values

$ hledger register tag:color="ancient white"

Note that if your tag value has a space in it, you must do something such as put quotation marks around the entire tag value. If there are no spaces in your tag value, the quotation marks are optional.

Combine comments and tags

Two options. The first is to write comments first, and put tags at the end:

2022/10/31 Grocery Store   
   Expenses     $3.52   ;  on sale today item:candy
   Liabilities:CreditCard

The second option is to put a comma after the tag, and then add a comment after the comma:

2022/10/31 Grocery Store   
   Expenses     $3.52   ;  item:candy, on sale today
   Liabilities:CreditCard

You can even have comment, tag, comma, and comment.

Use multiple tags on the same line

Separate multiple tags with a comma:

2022/05/20 AcmeWrappings.com  
    Expenses:Clothing  $58.99  ; fabric:wool, width:20, color:ancient white
    Liabilities:MonsterCard

Use the --pivot option to total expenses by tag value

$ hledger balance expenses --pivot color

or

$ hledger balance expenses --pivot fabric

Conclusion

Tag, you’re it.

A tax reporting setup

Tax prep is done in a subdirectory of the year.:

finance/
 2023/
  taxes/
   Health_Insurance_Form1095a_2023.pdf
   (1099s, etc..)
   next.journal -> ../../2024/2024.journal
   prev.journal -> ../../2022/2022.journal
   tax-reports.org.gpg

tax-reports.org (optionally encrypted with gpg) is the main thing; it is an emacs org outline shaped like the tax forms or tax preparation software (freetaxusa.com in my case).

For each form item/field, there's an outline heading, and under it one or more hledger reports designed to show (and explain) the required information. Here's an excerpt:

* tax preparation for 2023    -*- org-confirm-babel-evaluate:nil; -*-
...
** Income
*** overview
#+begin_src hledger :cmdline -f ../2???.journal -p 2023 bal -Y type:r cur:. -4 --invert
#+end_src

#+RESULTS:
#+begin_example
Balance changes in 2023:
...
#+end_example

*** Common Income
**** Interest Income
#+begin_src hledger :cmdline -f ../2???.journal -p 2023 bal -Y type:r interest --invert
#+end_src

#+RESULTS:
: Balance changes in 2023:
...

The reports are embedded as org source blocks. With cursor on the command line, C-c C-c runs the command and updates the RESULTS. (The two RESULTS above have different markup, because of output size - (setq org-babel-min-lines-for-block-output ...) to control this.)

The commands reference ../2???.journal (2023.journal in this case), and some of them use prev.journal or next.journal as well.

I used to define tax-specific account aliases renaming my usual account names to match the names used in tax forms/tax software, but that's work and another layer of confusion, so currently I don't bother with that.

I have used this setup for a number of years now and for me it gives a good combination of manageability, tweakability, repeatability, and persistence/memory across years.

For example, when starting tax prep for a year, I start with a copy of the previous year's outline, which includes all of last year's reports. As I press C-c C-c in each report, it updates to this year. This is useful for comparison and sanity checking. Each year, the process becomes easier.

(Discussion)

Converting time to money

Perhaps you track billable hours with hledger, and you'd like to see those hours as money. The method depends on where you store time records:

Journal files

If you might have a different rate for each job, and there aren't too many transactions per job: you could record the rate as a cost (2h @ $100), and run a cost report with -B/--cost.

When the rate doesn't change much and there are many transactions, it's more convenient to declare it with a P directive, and run a value report with -V/-X.

Timeclock files

You can't write P directives in timeclock format, but you can include the timeclock file(s) from a journal file, and write the directives there. (See below.)

Timedot files

Timedot files can't hold P directives, so include them from a journal file, which can. Also timedot amounts normally have no commodity symbol, making them hard to price; but you can give them one with the D directive:

# 2022-time.journal
D 1.0 h
P 2022-01-01 h $100
include 2022.timedot
# 2022.timedot
2022-01-01
a   ....
$ hledger -f 2022-time.journal bal 
               1.0 h  a
--------------------
               1.0 h  

$ hledger -f 2022-time.journal bal -V
                $100  a
--------------------
                $100  

If your rate changes, start a new timedot file:

# 2022-time.journal
D 1.0 h

P 2022-01-01 h $100
include 2022.timedot

P 2022-03-01 h $120
include 2022b.timedot
$ hledger -f 2022-time.journal bal -MVTA
Balance changes in 2022Q1, valued at period ends:

   ||  Jan  Feb   Mar    Total  Average 
===++===================================
 a || $100    0  $120     $220      $73 
---++-----------------------------------
   || $100    0  $120     $220      $73 

Time planning

Some notes relating to time tracking and budgeting.

See also

A time budgeting workflow

A summary of Simon's 2018-2019 time budgeting workflow:

I keep a hledger timedot file open in a text-mode Emacs in a drop-down iTerm hotkey window.

Each 15-minute chunk is logged with a dot. I group the dots into hours for quick visual scanning. I use comments as temporary reminders, if needed. Sometimes I use M-x align-regexp, [.;] to line up the dots (or comments)

2019-01-08
fos.hledger.sup          .
adm.email                ..
adm.finance              .... .... ..
fos.hledger.issues.941   .... .
fos.plaintextaccounting  ; 1045
biz.dev                  ; do such and such

I've trained myself to update this often while at the computer, and before walking away. If I forget, retroactive logging is also pretty easy. These help:

  • work in quarter/half/whole hour chunks, preferably synced with the clock
  • have a script or updating terminal pane showing today's sleep/wake/timelog-saved times, to jog the memory.

Not every day is the same; this system has been quick and flexible enough to suit a range of conditions.

How to set up a time budget

I can set daily/weekly/monthly time budgets if I want:

  • create a time.journal which includes your (timedot or timeclock) time log file (assuming you're not tracking time in journal format)
    # time.journal
    include time.timedot
    
  • choose a budget interval, eg daily, weekly or monthly
  • if you have some historical timelog data, review average spending on that interval to get a baseline
    $ hledger -f time.journal date:thisyear bal -WA
    
  • in time.journal add a periodic transaction rule to allocate budget amounts, similar to baseline, on that interval
    ~ weekly
        (adm:time)       1h
        (ser:some:proj)  4h
    
  • run a budget report, using the same interval:
    $ hledger -f time.journal bal --budget -W
    Balance changes in 2017/11/27w48:
    
                ||     2017/11/27w48 
    ===============++===================
    adm:time      || 0.25h [25% of 1h] 
    ser:some:proj || 0.75h [19% of 4h] 
    ---------------++-------------------
                ||             1.00h 
    

Simon's hledger time dashboard (2018)

Here's how I have been logging time for a few years. I have four files:

  1. time-2018.timedot is the current year's time log. It contains daily entries in timedot-ish format, like:

    2018/5/7
    adm.email             20m
    inc.client1.enh.1342  .... .
    inc.client1.enh.1188  .
    inc.client1.enh.1335  ..
    fos.hledger.support
    
  2. time.journal is in journal format so it can include multiple timedot file(s) and provide an account alias allowing period instead of colon in account names:

    ; allow . as subaccount separator in timedot files
    alias /\./=:
    
    ;include time-2016.timedot
    ;include time-2017.timedot
    include time-2018.timedot
    
  3. time-daily.budget defines some daily goals, optionally date-bounded, using periodic transaction(s):

    ~ daily  ; [from Y/M/D] [to Y/M/D]
        (adm)    1  ; your goals here
        (inc)    1
        (fos)    1
    
  4. time-weekly.budget defines some weekly goals. I like to set these independently of the above, which in current hledger means they must be in a separate file:

    ~ weekly  ; [from Y/M/D] [to Y/M/D]
        (adm)    1
        (biz)    1
        (inc)    1
        (fos)    1
    

The monthly budget reuses the weekly goals.

I have an iTerm2 Hotkey Window (a terminal that drops down on ALT-space) with six panes:

  1. a bash script showing latest laptop wake/sleep and timelog save times, as a memory aid for time logging:

    tlogwatch
    

    (Here's that script, useful only to mac users; please show me something better!):

     # record timelog update times
     export TIMELOG=$HOME/time-2018.timedot
     alias timelogsaved="ls -lT $TIMELOG | cut -d' ' -f8-11"
     alias timelogaccessed="ls -lTu $TIMELOG | cut -d' ' -f8-11"
     alias timelogcreated="ls -lTU $TIMELOG | cut -d' ' -f8-11"
     
     # show mac sleep/wake & display on/off  events
     alias wakelog="pmset -g log | egrep -E '((Sleep|Wake) +\t|Display is)' | sed -E \
           -e 's/(Notification|Sleep|Wake) *	//' \
           -e 's/is turned //' \
           -e 's/Entering Sleep state/Sleep/' \
           "
     
     # show recent wakeup/timelog save times to help with time logging, clipped to screen width
     # The width clipping is to help watch display this in dashboard.
     function tlog()
     {
       LINES=${1:-20}
       ( wakelog | tail -$LINES
         printf " \n"
         printf "$(timelogcreated) timelog created\n"
         printf "$(timelogsaved) timelog saved\n"
         printf "$(timelogaccessed) timelog accessed\n"
       ) | cut -c-$(expr $COLUMNS - 1)
     }
     
     # run a brief tlog report periodically, passing any args to watch
     function tlogwatch()
     {
       LINES=${1:-20}
       shift
       watch -t -n60 $@ "bash -ic 'tlog '$LINES"
     }
     # TODO why does "tlogwatch 10" give "sh: 10: command not found" ?
    
  2. a text-mode emacs for updating the time log:

    emacs -nw time-2018.timedot
    
  3. -5: updating (using entr) time budget reports for the current day/week/month, using hledger 1.9.1+:

    ls time.journal time-2018.timedot time-daily.budget  | entr sh -c 'clear; hledger -f time.journal -f time-daily.budget  bal --budget -1 -D date:today-tomorrow'
    
    ls time.journal time-2018.timedot time-weekly.budget | entr sh -c 'clear; hledger -f time.journal -f time-weekly.budget bal --budget -1 -W date:thisweek-nextweek'
    
    ls time.journal time-2018.timedot time-weekly.budget | entr sh -c 'clear; hledger -f time.journal -f time-weekly.budget bal --budget -1 -M date:thismonth-nextmonth'
    
  4. an updating hledger-ui for exploring time usage (shift-up/down to resize period, shift-left/right to step through time, t to return to today):

    hledger-ui --watch --change date:today -f time.journal
    

Track changes with version control

You don't need to do this, but it's a nice way to keep track of changes to your data.

git

Start tracking changes:
git init && git add 2017.journal && git commit 2017.journal -m "first commit"

View uncommitted changes: git status, git diff

Commit changes: git commit 2017.journal -m "updates"

View past commits: git log

darcs

darcs init && darcs add 2017.journal && darcs record 2017.journal -m "first commit"

darcs whatsnew, darcs diff

darcs record 2017.journal -m "updates"

darcs log

etc.

Track investments (2017)

You can use hledger to track stock investments. In fact, the double-entry accounting is flexible enough to support most constellations you will come across. However, you may find that some transactions could be better supported. Caveats are:

  • hledger does not validate the cost basis during a sale.
  • historical mark-to-market performance is not supported (but the market value at one instant, like today, can be calculated)

Example

Buying a stock

Let's go over a simple example using costs:

2017/1/1 opening balance
  (assets:depot)  $3000

2017/1/2 buy shares at $200
  ; let's assume no fees
  assets:shares   10 TSLA @ $200  ; transaction/purchase price
  assets:depot

Some reports. We start with $3000. After the 1/2 purchase, we have $1000 remaining and 10 TSLA shares:

$ hledger -f t.j bal --flat -HD
Ending balances (historical) in 2017/01/01-2017/01/02:

               ||  2017/01/01     2017/01/02
===============++============================
 assets:depot  ||       $3000          $1000
 assets:shares ||           0        10 TSLA
---------------++----------------------------
               ||       $3000 $1000, 10 TSLA

Show the shares' value at cost, with -B/--cost:

$ hledger -f t.j bal --flat -HD -B
Ending balances (historical) in 2017/01/01-2017/01/02:

               ||  2017/01/01  2017/01/02
===============++=========================
 assets:depot  ||       $3000       $1000
 assets:shares ||           0       $2000
---------------++-------------------------
               ||       $3000       $3000

Value reporting

Add the following to the journal file.

; market price, has jumped since yesterday's purchase!
P 2017/1/3 TSLA $250

Show the shares's value using the latest applicable market price, with -V/--value. A $500 capital gain is apparent in the totals:

$ hledger -f t.j bal --flat -HD -V
Ending balances (historical) in 2017/01/01-2017/01/02:

               ||  2017/01/01  2017/01/02
===============++=========================
 assets:depot  ||       $3000       $1000
 assets:shares ||           0       $2500
---------------++-------------------------
               ||       $3000       $3500

There are still limitations in the value reporting that hledger can currently do. More information can be found in Github issue #131 and Github issue #329.

You may want to investigate the output after adding more prices to the journal file.

P 2017/1/1 TSLA $210
P 2017/1/4 TSLA $250
P 2017/1/8 TSLA $270

Selling a stock and tracking capital gains

At some point you will probably sell shares. It may seem intuitive to model such a sale as follows.

2017/1/4 sell shares at $250      ; NOTE: You probably want to model capital gains too; see below
  assets:shares   -10 TSLA @ $250  ; sell price
  assets:depot

This leads to the following evolution

hledger -f t.j balance --flat -HD
Ending balances (historical) in 2017/01/01-2017/01/04:

               ||  2017/01/01     2017/01/02     2017/01/03  2017/01/04
===============++=======================================================
 assets:depot  ||       $3000          $1000          $1000       $3500
 assets:shares ||           0        10 TSLA        10 TSLA           0
---------------++-------------------------------------------------------
               ||       $3000 $1000, 10 TSLA $1000, 10 TSLA       $3500

You end up with the correct amount in your depot. At some point, however, you will have to report the capital gain that you realized with your sale. This gain is currently invisible. In fact, we have violated the double-entry principle and created money out of nowhere.

Let's report our sale in a different way.

2017/1/4 sell shares at $250
  assets:shares         -10 TSLA @ $200  ; cost basis (must be tracked by user!)
  assets:depot          $2500            ; cash proceeds
  revenue:capital_gains                  ; deduce profit

Now, the new $500 are correctly balanced with the capital gains account.

hledger -f t.j balance --flat -HD
Ending balances (historical) in 2017/01/01-2017/01/04:

                       ||  2017/01/01     2017/01/02     2017/01/03  2017/01/04
=======================++=======================================================
 assets:depot          ||       $3000          $1000          $1000       $3500
 assets:shares         ||           0        10 TSLA        10 TSLA           0
 revenue:capital_gains ||           0              0              0       $-500
-----------------------++-------------------------------------------------------
                       ||       $3000 $1000, 10 TSLA $1000, 10 TSLA       $3000

Further reading

Track investments (2020)

Here's a tutorial on tracking "investments" - stocks, cryptocurrencies, and similar - in hledger. This is a more in-depth version of Tracking investments, using hledger 1.17; older hledger versions may not match this doc. I hope to teach you a little basic investment accounting, or a little about doing it with hledger (and other PTA tools), or a little of both. Notably, you'll see three different ways to record lots and capital gains:

  1. using {} notation (works in Ledger, Beancount)

  2. using just @ notation (works in hledger, Ledger, Beancount..)

  3. using just standard postings (works in any double-entry accounting system).

A stock purchase

We'll use a cryptocurrency and fictitious prices here, but these examples apply equally well to stocks. Let's say we start the year with $1000 for investment:

2020-01-01 opening balances
    assets:bank:checking             $1000
    equity:opening/closing balances

In february we see ADA is priced at $0.02, and decide to buy a little. We'll spend $40, which at 2 cents per ADA buys 2000 ADA. Here's the most straightforward journal entry:

2020-02-01 buy ada
    assets:cc:ada          2000 ADA
    assets:bank:checking   -$40

We paid $40 (from one asset) and received 2000 ADA in exchange (another asset). The exact account names don't matter; here we've used a cc subaccount to group all cryptocurrencies, and an additional ada subaccount to distinguish ADA holdings from other cryptocurrencies (this can be helpful in some reports.)

In real life there'll probably be another posting or two, for transaction expenses. We'll omit those for now.

There are a few more things to say about this entry...

How does that balance ?

As you may know, in hledger and other PTA tools the primary rule is:

  • "Each transaction must balance - its amounts must add up to zero".

Well, in hledger (and Ledger), there's another rule:

  • "When a transaction's unbalanced amounts involve exactly two commodities, assume one is converted to the other with a conversion rate that makes the transaction balance."

We can use hledger print -x (x for explicit) to show the above with all amounts and the inferred conversion price:

$ hledger print -x
2020-02-01
    assets:cc:ada           2000 ADA @@ $40
    assets:bank:checking    $-40

This @ notation has two forms: @@ TOTALPRICE or @ UNITPRICE. In hledger we call it "cost" (generically for both purchases and sales); it records the price/conversion rate that was used in this specific transaction.

Note this more explicit journal entry has some redundancy, but that's no harm - it makes things clearer to the human reader, it provides an extra test of the tool's rounding and precision, and it helps guard against typos. So we'll use this one.

But here are two more ways of writing the above, just for completeness:

2020-02-01
    assets:cc:ada          2000 ADA @@ $40
    assets:bank:checking                   ; $-40 inferred here
2020-02-01
    assets:cc:ada          2000 ADA @ $0.02
    assets:bank:checking                    ; -(2000 x $0.02) = $-40 inferred here

A more correct entry

Do you find this still wrong ? Some amount of dollars seems to disappear, and some amount of ADA appears. Doesn't this violate the accounting equation, the foundation of double entry bookkeeping, which essentially says "money is not created out of thin air" ? #1177 agrees with you; in summary, it seems the correct entry for this transaction is more like:

2020-02-01
    assets:cc:ada          2000 ADA
    equity:conversion     -2000 ADA
    equity:conversion       $40
    assets:bank:checking   -$40

This entry might look a little strange to you, but it does show more clearly that the transaction is balanced; it gathers commodity conversions into a single "conversion" account which can provide useful information; and it preserves the accounting equation. The "conversion" account name isn't special, but it's an equity account. Another way to write it, relying on automatic transaction balancing, is:

2020-02-01
    assets:cc:ada          2000 ADA
    assets:bank:checking   -$40
    equity:conversion                 ; -2000 ADA, +$40 inferred here

There's a problem, however: hledger (and other PTA tools) do not recognise these more general forms as a commodity conversion, so will not be able to show cost reports (with -B, at least..). For this reason, and considering that the unbalanced accounting equation often does not affect your everyday reports, you might prefer to stick with the @ notation.

Or, use both equity postings and @ notation together, which might give the best of both worlds.

What about lots ?

If you know a little about investing, or have read Ledger or Beancount docs, you'll know that it's important to track the date and cost of each stock purchase, or lot. This information is needed:

  • While holding investments, to calculate unrealised capital gains/losses, ie the change in their value "on paper" as market prices fluctuate.

  • When selling an investment, so that:

    1. We can select the right lots to sell. Tax law may require, eg, selling the oldest lots first (the FIFO strategy - First In, First Out).

    2. We can calculate the realised capital gain/loss from this sale, by comparing the selling price with the original cost (AKA cost basis) of those lots.

Ledger and Beancount provide a special syntax and some builtin reports for tracking lots and calculating capital gains. Currently, hledger does not (aside from a little support for lot syntax). So for now, how can we track lots in hledger ?

We can use the obvious categorisation feature: accounts. We'll give every lot its own uniquely-named subaccount. Since we already have an account just for ADA, we'll just name the lot subaccounts by the lots' purchase dates. If doing multiple ADA purchases per day, we could add a sequence number. We could also include the cost in the name, if we want extra clarity.

So, let's amend the above journal entry, adding the 20200201 subaccount to represent this first lot:

2020-02-01
    assets:cc:ada:20200201    2000 ADA @@ $40
    assets:bank:checking      $-40

Review

So far, what have we got ? The journal is :

2020-01-01 opening balances
    assets:bank:checking                       $1000
    equity:opening/closing balances

2020-02-01
    assets:cc:ada:20200201                      2000 ADA @@ $40
    assets:bank:checking                        $-40

The balance sheet (truncated for brevity) shows assets are $960 and 2000 ADA:

$ hledger bs --flat | head -10
Balance Sheet 2020-02-01

                        ||     2020-02-01 
========================++================
 Assets                 ||                
------------------------++----------------
 assets:bank:checking   ||           $960 
 assets:cc:ada:20200201 ||       2000 ADA 
------------------------++----------------
                        || $960, 2000 ADA 

(| head -10 is used here just to hide the empty Liabilities section. You can omit it if it doesn't work on your system.)

And with -B/--cost (B for cost basis) we see costs so far are:

$ hledger bs --flat -B |head -10
Balance Sheet 2020-02-01, valued at cost

                        || 2020-02-01 
========================++============
 Assets                 ||            
------------------------++------------
 assets:bank:checking   ||       $960 
 assets:cc:ada:20200201 ||        $40 
------------------------++------------
                        ||      $1000 

Market prices

We'll also start recording the prevailing market price that was in effect at each point in time. This will allow us to report on the market value of our investments.

We don't need to know it every day or every hour; for this example, we'll just record it at the start of each month. hledger will assume that market price through the month, until the next one is declared.

Here's the P (market Price) directive declaring that the market price of ADA, in dollars, on 2020-02-01, was $0.02:

P 2020-02-01 ADA $0.02

And let's say that ADA's price has doubled by march 1st; we'll record that too:

P 2020-03-01 ADA $0.04

We'll add these to the main journal for simplicity. Or if you prefer you can keep them in a separate file, eg 2020.prices, adding include 2020.prices to the main journal.

Note that the initial $0.02 market price, and the $0.02 transaction price we recorded above, are the same in this example. In real life they may not be exactly the same, but normally they will be quite similar.

Value reports; some gotchas

Now we have enough data to do a little value reporting. This can be a little confusing at first, so here are a few examples.

Our journal so far is:

2020-01-01 opening balances
    assets:bank:checking                       $1000
    equity:opening/closing balances

2020-02-01
    assets:cc:ada:20200201                      2000 ADA @@ $40
    assets:bank:checking                        $-40

P 2020-02-01 ADA $0.02
P 2020-03-01 ADA $0.04

Let's check the current market value (AKA mark to market) of our holdings. -V is a simple form of the --value flag:

$ hledger bs --flat -V |head -10
Balance Sheet 2020-02-01, current value

                        || 2020-02-01 
========================++============
 Assets                 ||            
------------------------++------------
 assets:bank:checking   ||    $960.00 
 assets:cc:ada:20200201 ||     $80.00 
------------------------++------------
                        ||   $1040.00 

But the ADA market value looks wrong - on 2020-02-01 it was $40, not $80. So it's wise to check the manual. In particular, note: "For single period reports, the valuation date is today (equivalent to --value=now)". So even though the report's end date is 2020-02-01 (the date of the last transaction), hledger picked "now" as the valuation date, and therefore used our latest 2020-03-01 P directive.

We can fix this by specifying an explicit report end date, which also sets the valuation date. We'll use -e to specify 2020-02-01:

$ hledger bs --flat -V -e 2020-02-01 |head -10
Balance Sheet 2020-01-31, current value

                      || 2020-01-31 
======================++============
 Assets               ||            
----------------------++------------
 assets:bank:checking ||   $1000.00 
----------------------++------------
                      ||   $1000.00 
======================++============

Now there's no ADA balance at all - what gives ? Remember that end dates are exclusive, so the ADA purchase on 2020-02-01 is excluded. With a later end date, eg 2020-02-02, we see it:

$ hledger bs --flat -V -e 2020-02-02 |head -10
Balance Sheet 2020-02-01, current value

                        || 2020-02-01 
========================++============
 Assets                 ||            
------------------------++------------
 assets:bank:checking   ||    $960.00 
 assets:cc:ada:20200201 ||     $40.00 
------------------------++------------
                        ||   $1000.00 

And if we specify an end date after the second price directive, we'll see the value at that date:

$ hledger bs --flat -V -e 2020-03-02 |head -10
Balance Sheet 2020-03-01, current value

                        || 2020-03-01 
========================++============
 Assets                 ||            
------------------------++------------
 assets:bank:checking   ||    $960.00 
 assets:cc:ada:20200201 ||     $80.00 
------------------------++------------
                        ||   $1040.00 

Often, a multiperiod, eg monthly, report makes things clearer. For this case the manual says: "valuation date ... for multiperiod reports, it is the last day of each subperiod". For this example we still need to specify the end date though, otherwise the report will stop at the end of the month containing the last transaction (ie, 2020-02-29). This time we'll say -e apr (or -e 202004), which is less typing and includes all of march:

$ hledger bs --flat -M -V -e apr | head -10
Balance Sheet 2020-01-31,,2020-03-31, valued at period ends

                        || 2020-01-31  2020-02-29  2020-03-31 
========================++====================================
 Assets                 ||                                    
------------------------++------------------------------------
 assets:bank:checking   ||   $1000.00     $960.00     $960.00 
 assets:cc:ada:20200201 ||          0      $40.00      $80.00 
------------------------++------------------------------------
                        ||   $1000.00    $1000.00    $1040.00 

Finally, we can clearly see the value of our holdings over time. No ADA, just dollars, in january; ADA worth $40 when purchased, in february; and worth $80, thanks to the increase in market price, in march.

Just for comparison, here's the same report but showing cost instead of value. Of course cost is not affected by market prices:

$ hledger bs --flat -M -B -e apr | head -10
Balance Sheet 2020-01-31,,2020-03-31, valued at cost

                        || 2020-01-31  2020-02-29  2020-03-31 
========================++====================================
 Assets                 ||                                    
------------------------++------------------------------------
 assets:bank:checking   ||   $1000.00     $960.00     $960.00 
 assets:cc:ada:20200201 ||          0      $40.00      $40.00 
------------------------++------------------------------------
                        ||   $1000.00    $1000.00    $1000.00 

Unrealised capital gain

The difference between the $40 purchase cost of the ADA, and its $80 value in march, is an unrealised capital gain. "Unrealised" (and therefore not yet taxable, typically) because we haven't yet sold the ADA and captured the gain in our base currency.

A sale

The next day, we decide to sell all the ADA, just to test the process and capture a little profit. Assuming that this will look much like the purchase transaction in reverse, using the @ notation again, we come up with this:

2020-03-02 sell all ada
    assets:cc:ada:20200201  -2000 ADA @ $0.04
    assets:bank:checking      $80

For a little extra error checking, this time we used the @ UNITPRICE form, so we can visually check that the per-unit cost looks correct (at or close to the market price).

Here's the new balance sheet, with -E (empty) to make it show the now empty ada account:

$ hledger bs --flat -e apr -E | head -10
Balance Sheet 2020-03-31

                        || 2020-03-31 
========================++============
 Assets                 ||            
------------------------++------------
 assets:bank:checking   ||   $1040.00 
 assets:cc:ada:20200201 ||          0 
------------------------++------------
                        ||   $1040.00 

Realised capital gain

Our dollar balance has increased, from $1000 to $1040, but somewhat magically - there seems to be no transaction causing it. This seems like a bad sign. And indeed a full balance sheet including equity shows a non-zero total, confirming that the Accounting Equation has been disturbed:

$ hledger bse --flat
Balance Sheet With Equity 2020-03-02

                                 || 2020-03-02 
=================================++============
 Assets                          ||            
---------------------------------++------------
 assets:bank:checking            ||   $1040.00 
---------------------------------++------------
                                 ||   $1040.00 
=================================++============
 Liabilities                     ||            
---------------------------------++------------
---------------------------------++------------
                                 ||            
=================================++============
 Equity                          ||            
---------------------------------++------------
 equity:opening/closing balances ||   $1000.00 
---------------------------------++------------
                                 ||   $1000.00 
=================================++============
 Net:                            ||     $40.00 

$40 has appeared from somewhere. This increase is the realised capital gain, which is considered a revenue. If we had sold at a lower price than we paid, this number would be negative, representing a capital loss, which is an expense.

We want this gain/loss to be recorded in the journal, to satisfy the accounting equation and keep accurate records, and also because it is typically a taxable event; we'll need to know all of these revenues/expenses when filing taxes.

Recording capital gain

The sale transaction above is balanced, with no room for an extra revenue posting. If we try, hledger complains:

2020-03-02 sell all ada
    assets:cc:ada:20200201                     -2000 ADA @ $0.04 = 0 ADA
    assets:bank:checking                         $80
    revenues:capital gain                       $-40
$ hledger print
...
could not balance this transaction (real postings are off by $-40.00)
...

Ledger (and Beancount) will accept an entry like this, if you add a special {} notation identifying the lot's original cost. Below, note the extra {$0.02}, which says "this is a lot, and was purchased at $0.02 each". Ledger will calculate the expected capital gain of $40 and will consider this transaction to be balanced:

2020-03-02
    assets:cc:ada:20200201                     -2000 ADA {$0.02} @ $0.04 = 0 ADA
    assets:bank:checking                         $80
    revenues:capital gain                       $-40

But hledger doesn't know about lots or capital gains, as mentioned. (hledger 1.17.99+ will parse the {} notation, but ignores it.) So how can we model a stock sale in hledger ? In general, we should:

  1. convert back to cash using the lot's cost price
  2. manually calculate the capital gain (difference of cost and selling price) and record it as a revenue/expense

If we used the @ notation for the purchase, we should use it here too. The sale looks like this:

2020-03-02 sell ada
    assets:cc:ada:20200201                     -2000 ADA @ $0.02  ; the original cost
    revenues:capital gain                       $-40              ; the capital gain, 2000 x ($0.04-$0.02)
    assets:bank:checking                         $80              

Or if we used the more correct entry for the purchase, ie just standard double entry bookkeeping postings, the sale looks like this:

2020-03-02 sell ada
    assets:cc:ada:20200201                     -2000 ADA
    equity:conversion                           2000 ADA
    equity:conversion                           $-40       ; the original cost
    revenues:capital gain                       $-40       ; the capital gain
    assets:bank:checking                         $80              

If we check the bse report now, we'll still see a $40 total, but this is expected because a revenue has been recorded and not yet merged into equity by "closing the books". If we were to do that temporarily:

2020-03-02 close the books, just for testing
    revenues:capital gain                        $40 = $0
    equity:retained earnings                    -$40

We would see the proper zero total:

$ hledger bse --flat 
Balance Sheet With Equity 2020-03-02

                                 || 2020-03-02 
=================================++============
 Assets                          ||            
---------------------------------++------------
 assets:bank:checking            ||   $1040.00 
---------------------------------++------------
                                 ||   $1040.00 
=================================++============
 Liabilities                     ||            
---------------------------------++------------
---------------------------------++------------
                                 ||            
=================================++============
 Equity                          ||            
---------------------------------++------------
 equity:opening/closing balances ||   $1000.00 
 equity:retained earnings        ||     $40.00 
---------------------------------++------------
                                 ||   $1040.00 
=================================++============
 Net:                            ||          0 

A variation: some discussions on the web suggest transferring directly from equity, bypassing revenues. In that case we would need to remember to include it in our tax reports, eg we would need to look at hledger bal revenue expenses 'capital gain' instead of just hledger is.





Inventory accounting

Fundraiser

This example demonstrates accounting for a fundraiser where members sold meat-sticks and chocolate bars. The local organization would check out inventory, sell it, and then pay the regional organization back for the fundraising goods they sold.

In this particular example chocolate bars are sold for $2 and meat-sticks are sold for $1. The local organization gets to keep 30% of the proceeds.

The role of the first three accounts are fairly straight-forward. assets:cash tracks money in the cash box. assets:inventory tracks the unsold meat and chocolate stock. liabilities:supplier tracks the money owed to the supplier.

The interesting account is income:fundraiser. This account summarizes the effect of the fundraiser on the organization. In our example the initial effect on the organization is that it acquired $61.60 of debt, 24 meat-sticks, and 32 chocolate bars. As the fundraiser progresses, this account will hold only the $26.40 of income.

In the real use-case, the members of the organization would check out product to sell. Additional transactions can be used to transfer stock from the organization's inventory accounts to member-specific accounts.

account assets:cash           ; cash
account assets:inventory      ; product inventory
account liabilities:supplier  ; money owed to supplier
account income:fundraiser     ; income attributed to the fundraiser

commodity MEAT  ; meat-sticks
commodity CHOC  ; chocolate bars
commodity USD   ; US dollars

; Price of the fundraiser goods at the supplier's price
P 1-1 MEAT 0.70 USD
P 1-1 CHOC 1.40 USD

1-1 * Supplier | Pick up inventory
    assets:inventory              24 MEAT
    assets:inventory              32 CHOC
    liabilities:supplier      -61.60 USD
    income:fundraiser

1-2 * Alice | Sold some goods
    assets:inventory             -20 MEAT
    assets:inventory             -12 CHOC
    assets:cash                   44 USD
    income:fundraiser

1-3 * Bob | Sold more things
    assets:inventory              -4 MEAT
    assets:inventory             -20 CHOC
    assets:cash                   44 USD
    income:fundraiser

1-4 * Supplier | Pay balance
    liabilities:supplier       61.60 USD
    assets:cash               -61.60 USD

With a daily balance change and historical balance reports we can see the effect of each transaction. Over time we'll be converting inventory into profits.

$ hledger bal -D --pretty
Balance changes in 2021-01-01..2021-01-04:

                      ║                    2021-01-01                    2021-01-02                   2021-01-03  2021-01-04
══════════════════════╬══════════════════════════════════════════════════════════════════════════════════════════════════════
 assets:cash          ║                             0                     44.00 USD                    44.00 USD  -61.60 USD
 assets:inventory     ║              32 CHOC, 24 MEAT            -12 CHOC, -20 MEAT            -20 CHOC, -4 MEAT           0
 income:fundraiser    ║ -32 CHOC, -24 MEAT, 61.60 USD  12 CHOC, 20 MEAT, -44.00 USD  20 CHOC, 4 MEAT, -44.00 USD           0
 liabilities:supplier ║                    -61.60 USD                             0                            0   61.60 USD
──────────────────────╫──────────────────────────────────────────────────────────────────────────────────────────────────────
                      ║                             0                             0                            0           0

hledger bal -DH --pretty
Ending balances (historical) in 2021-01-01..2021-01-04:

                      ║                    2021-01-01                    2021-01-02  2021-01-03  2021-01-04
══════════════════════╬═════════════════════════════════════════════════════════════════════════════════════
 assets:cash          ║                             0                     44.00 USD   88.00 USD   26.40 USD
 assets:inventory     ║              32 CHOC, 24 MEAT               20 CHOC, 4 MEAT           0           0
 income:fundraiser    ║ -32 CHOC, -24 MEAT, 61.60 USD  -20 CHOC, -4 MEAT, 17.60 USD  -26.40 USD  -26.40 USD
 liabilities:supplier ║                    -61.60 USD                    -61.60 USD  -61.60 USD           0
──────────────────────╫─────────────────────────────────────────────────────────────────────────────────────
                      ║                             0                             0           0           0                  

To see the same information in terms of the cash-value of the inventory that we're holding we can use the -V flag. From this view we can see the income realized by the fundraiser as it progresses, and the cash value of our current inventory.

hledger bal -DV --pretty
Balance changes in 2021-01-01..2021-01-04, valued at period ends:

                      ║ 2021-01-01  2021-01-02  2021-01-03  2021-01-04
══════════════════════╬════════════════════════════════════════════════
 assets:cash          ║          0   44.00 USD   44.00 USD  -61.60 USD
 assets:inventory     ║  61.60 USD  -30.80 USD  -30.80 USD           0
 income:fundraiser    ║          0  -13.20 USD  -13.20 USD           0
 liabilities:supplier ║ -61.60 USD           0           0   61.60 USD
──────────────────────╫────────────────────────────────────────────────
                      ║          0           0           0           0

$ hledger bal -DHV --pretty
Ending balances (historical) in 2021-01-01..2021-01-04, valued at period ends:

                      ║ 2021-01-01  2021-01-02  2021-01-03  2021-01-04
══════════════════════╬════════════════════════════════════════════════
 assets:cash          ║          0   44.00 USD   88.00 USD   26.40 USD
 assets:inventory     ║  61.60 USD   30.80 USD           0           0
 income:fundraiser    ║          0  -13.20 USD  -26.40 USD  -26.40 USD
 liabilities:supplier ║ -61.60 USD  -61.60 USD  -61.60 USD           0
──────────────────────╫────────────────────────────────────────────────
                      ║          0           0           0           0

For our fundraiser the members got credit for their contribution to the fundraiser. By pivoting by payee we can see the gross revenue and inventory sold by each member. The row for the supplier shows the total inventory acquired and cost paid.

hledger bal income:fundraiser --pivot=payee --pretty -Y
Balance changes in 2021:

          ║                          2021
══════════╬═══════════════════════════════
 Alice    ║  12 CHOC, 20 MEAT, -44.00 USD
 Bob      ║   20 CHOC, 4 MEAT, -44.00 USD
 Supplier ║ -32 CHOC, -24 MEAT, 61.60 USD
──────────╫───────────────────────────────
          ║                    -26.40 USD

To see the net revenue attributed to each member, we can generate the same report valued by the cost of the goods.

hledger bal income:fundraiser --pivot=payee --pretty -YV
Balance changes in 2021, valued at period ends:

       ║       2021
═══════╬════════════
 Alice ║ -13.20 USD
 Bob   ║ -13.20 USD
───────╫────────────
       ║ -26.40 USD

Tutorial: hledger basics

Here we'll walk you through a simple way of using hledger, the command line interface and built-in add command. This works on all platforms with no further setup. We'll describe every step in detail.

We'll also introduce some basic hledger concepts. So even if you don't plan on using the hledger CLI or add command, it might be worth skimming this tutorial.

Install hledger and let's get started!

Check your installation

Open a terminal or command prompt, and check your hledger version. It should be reasonably up to date. This doc was last tested with:

$ hledger --version
hledger 1.29

Locate your journal file with "hledger stats"

hledger reads financial transactions from a "journal file" (so named because it represents a General Journal).

By default, it looks for the journal file in your home directory, with filename .hledger.journal. This is good for getting started, but at some point you might want to customise this by setting the LEDGER_FILE environment variable. In the following examples, we'll assume the default file is being used.

Check your journal file using the stats command. You should see something like:

$ hledger stats
The hledger journal file "/home/YOU/.hledger.journal" was not found.
Please create it first, eg with "hledger add" or a text editor.
Or, specify an existing journal file with -f or LEDGER_FILE.

Most hledger commands read this file but can not change it; the add and web commands can also write it.

(If stats reports that the file exists, eg because you previously created it, move it out of the way temporarily for these exercises.)

Record a transaction with "hledger add"

Follow the help and use the add command to record your first transaction, an imaginary purchase at the supermarket. We'll go through this in detail. Later you'll learn other ways to enter data.

$ hledger add
Creating hledger journal file "/home/YOU/.hledger.journal".
Adding transactions to journal file /home/YOU/.hledger.journal
Any command line arguments will be used as defaults.
Use tab key to complete, readline keys to edit, enter to accept defaults.
An optional (CODE) may follow transaction dates.
An optional ; COMMENT may follow descriptions or amounts.
If you make a mistake, enter < at any prompt to restart the transaction.
To end a transaction, enter . when prompted.
To quit, enter . at a date prompt or press control-d or control-c.
Date [2023-05-25]:

add prompts for each transaction field. The first is the date. The value in square brackets is the suggested default (today's date). Press enter to accept it.

Description: trip to the supermarket

Transactions have an optional description (a single line of text) to help you understand them. You can describe the transaction here, or put a payee name, or leave it blank. Type trip to the supermarket and press enter.

Account 1: expenses

Transactions have two or more accounts. Keep it simple; just enter expenses for the first one.

If you're thinking "expenses sounds more like a category": it is, but double entry accounting calls those "accounts", too. A purchase is a transfer of money from an asset account to an expense account. An asset is something you own, like some money in a bank account or in your pocket. Once the money has been "moved" to an expense, you no longer own it, but the increasing balance in the expense account reminds you where it went.

Amount  1: $10

The amount being "moved" to expenses. You can use any currency or commodity symbol here; for this example we'll assume 10 US dollars.

Account 2: assets

Next, specify which account the money comes from. Just say assets.

Amount  2 ? [$-10.0]: 

Now you're asked for the amount to "move" to or from the assets account. As the default, hledger offers the amount required to "balance" the postings entered so far. The minus sign indicates the money is moving from this account. (hledger uses the positive and negative sign instead of accounting's traditional "debit" and "credit" terminology.) In a balanced transaction, the sum of posted amounts is zero, in other words no money disappears into thin air. Press enter to accept the default. It has an extra decimal place, but never mind.

Account 3 (or . or enter to finish this transaction): .

Press enter to finish entering this transaction.

2023-05-25 trip to the supermarket
    expenses             $10
    assets              $-10

Save this transaction to the journal ? [y]:

You are given a chance to review the transaction just entered. Here you see hledger's plain text data format for journal entries: a non-indented YYYY-MM-DD date, space, and description, followed by two or more indented posting lines, each containing an account name, two or more spaces, and an amount. (Account names can contain spaces, so at least two spaces are needed to separate them from the amount.) Press enter to save the journal entry.

Saved.
Starting the next transaction (. or ctrl-D/ctrl-C to quit)
Date [2023-05-25]: <CTRL-D>

hledger has saved it to the journal file and is ready for the next entry. Hold down the control key and press d once to exit.

stats should now report that your journal exists and contains one transaction:

$ hledger stats
Main journal file        : /home/YOU/.hledger.journal
Included journal files   : 
Transactions span        : 2023-05-25 to 2023-05-26 (1 days)
Last transaction         : 2023-05-25 (0 days ago)
Transactions             : 1 (1.0 per day)
Transactions last 30 days: 1 (0.0 per day)
Transactions last 7 days : 1 (0.1 per day)
Payees/descriptions      : 1
Accounts                 : 2 (depth 1)
Commodities              : 1 ($)
Market prices            : 0 ()

Run time (throughput)    : 0.13s (8 txns/s)

Show transactions with "hledger print"

The print command shows a tidied-up view of the transaction entries in your journal. Since there's just one so far, you should see:

$ hledger print
2023-05-25 trip to the supermarket
    expenses             $10
    assets              $-10

Examine your journal file

List and print the journal file (on Windows, use dir and type and the file path from hledger stats):

$ ls -l ~/.hledger.journal
-rw-r--r--  1 YOU  YOU  114 May 25 16:55 /home/YOU/.hledger.journal
$ cat ~/.hledger.journal
; journal created 2023-05-25 by hledger

2023-05-25 trip to the supermarket
    expenses             $10
    assets              $-10

Edit the journal file

Since the journal file is plain text, you can edit it directly with any text editor. Let's open the file in a text editor and test the effect of some changes. Eg:

$ emacs ~/.hledger.journal

A convenience: inferred amounts

First, remove one of the amounts, and save the file. Eg:

2023-05-25 trip to the supermarket
    expenses             $10
    assets

You'll see that hledger print still works (and shows just the one amount). This is a convenience to make manual data entry easier: if one amount is missing, hledger infers the amount needed to balance the transaction ($-10 in this case). To make such inferred amounts explicit, you can use hledger print -x.

Only one amount can be inferred

Now try removing both amounts, and save the file. This is an invalid entry:

2023-05-25 trip to the supermarket
    expenses
    assets

hledger print now gives an error:

$ hledger print
hledger: Error: /Users/simon/.hledger.journal:3-5:
3 | 2023-05-25 trip to the supermarket
  |     expenses
  |     assets

This transaction is unbalanced.
There can't be more than one real posting with no amount.
(Remember to put two or more spaces between account and amount.)

All hledger commands expect the journal to be well-formed, and will report an error and exit otherwise.

Two spaces

Notice the last part of that error message: "Remember to put two or more spaces between account and amount". Another cause of this error is forgetting to put two spaces before the amount, like this:

2023-05-25 trip to the supermarket
    expenses $10  ; <- problem: only one space between expenses and $10
    assets
$ hledger print
hledger: Error: /Users/simon/.hledger.journal:3-5:
3 | 2023-05-31 trip to the supermarket
  |     expenses $10
  |     assets

This transaction is unbalanced.
There can't be more than one real posting with no amount.
(Remember to put two or more spaces between account and amount.)

Since account names can contain spaces, you must separate them from anything following them (like an amount, or a comment) by two or more spaces. (Otherwise hledger thinks the account name is "expenses $10".)

Unbalanced transactions

Edit the file to look like this:

2023-05-25 trip to the supermarket
    expenses           $10
    assets             $10  ; <- another problem: both amounts are positive

Here, we wrote both posting amounts but got the sign wrong on one of them, so they don't add up to zero. hledger should detect this mistake. Verify it by running some command, eg print. You should see:

$ hledger print
hledger: Error: /Users/simon/.hledger.journal:3-5:
3 | 2023-05-31 trip to the supermarket
  |     expenses             $10
  |     assets               $10

This transaction is unbalanced.
The real postings all have the same sign. Consider negating some of them.
Consider adjusting this entry's amounts, or adding missing postings.

That makes sense. (It calls them "real" postings because there are some other kinds of posting you haven't learned about yet; they aren't important.)

Correct the mistake by adding the minus sign, or just removing the assets amount entirely, and verify that print works again:

$ hledger print
2023-05-25 trip to the supermarket
    expenses             $10
    assets              $-10

Record a transaction by editing

Edit the file again and manually add a second purchase transaction. It's often quickest to copy & paste a similar entry, then change it. Make the file look like this:

2023-05-25 trip to the supermarket
    expenses           $10
    assets            $-10

2023-05-26 forgot the bread
    expenses            $5
    assets

The blank line between transactions is customary, though not required. Test your work with print. You should see:

$ hledger print
2023-05-25 trip to the supermarket
    expenses             $10
    assets              $-10

2023-05-26 forgot the bread
    expenses              $5
    assets

What's in a Transaction ?

Here's a hledger journal entry for a simple transaction, with the parts named:

hledger basic transaction, showing names of parts

And here's a more complicated journal entry:

hledger complicated transaction with names of parts

Show postings and a running total with "hledger register"

The register command shows transactions in a different format. More precisely, it shows postings. Remember, a posting is an increase or decrease of some account by some amount, and a transaction contains two or more of them. Run register and compare with the output of print above. You should see (more or less, depending on your terminal width):

$ hledger register
2023-05-25 trip to the super..  expenses                       $10           $10
                                assets                        $-10             0
2023-05-26 forgot the bread     expenses                        $5            $5
                                assets                         $-5             0

Each line represents one account posting within a transaction. The transaction's date and description is displayed only for the first posting in each transaction. Next we see the posted account's name and the amount posted. The final column is a running total of the posted amounts, starting from zero.

Show a per-account register report

Notice how the running total above keeps resetting to zero. This makes sense (since we know each transaction's postings add up to zero) but isn't very useful. The register report is more useful when we restrict it to a subset of postings - say, only the postings within a single account. You can do this by specifying the account name as a command line argument.

Run a register report for the expenses account. You should see something like the below. (On POSIX platforms, this command uses the terminal width so the output may look slightly different. You can force it to look like the below by running export COLUMNS=80 first:

$ hledger register expenses
2023-05-25 trip to the super..  expenses                       $10           $10
2023-05-26 forgot the bread     expenses                        $5           $15

Now it's clear that your expenses balance - ie, the total amount spent - has increased to $15.

Your assets balance should have dropped accordingly. Check it:

$ hledger register assets
2023-05-25 trip to the super..  assets                        $-10          $-10
2023-05-26 forgot the bread     assets                         $-5          $-15

Set initial account balances

hledger assumes every account starts with a zero balance, so in the previous example, we see the withdrawals producing a negative running balance. Let's assume assets represents a real-world asset, like your bank checking account, and you want to start tracking it from 2023-05-01 onward, and on that day it contained exactly $500. To show the real-world account balance, edit your journal file and add this transaction at the top:

2023-05-01 set initial assets balance
    assets                              $500
    equity:opening balances

The other account name doesn't matter too much; equity:opening balances is conventional. (You could also use an unbalanced transaction for this if you prefer.) Now the report looks like this, with an accurate running balance on each date (hledger calls this a historical balance):

$ hledger register assets
2023-05-01 set initial asset..  assets                        $500          $500
2023-05-25 trip to the super..  assets                        $-10          $490
2023-05-26 forgot the bread     assets                         $-5          $485

Queries

The account name argument above is an example of a query, a search pattern which restricts a report to a subset of the data. In this way you can make reports focussed on just the data you care about.

Note that the argument is a case-insensitive regular expression which matches anywhere inside the account name. So "e" would match both expenses and assets.

And if you had an account named other assets, "assets" would also match that, so to match only the assets account you'd need a more precise pattern like "^assets$". (In a regular expression ^ means "match at the beginning" and $ means "match at the end".) If this doesn't make sense, read more about regular expressions.

Multiple query arguments are usually combined as follows: queries on the same field are ORed, and queries on different fields are ANDed. See the linked query doc for more details.

Run the following examples and make sure they make sense, consulting the manual as needed.

Show only transactions whose description contains "bread":

$ hledger print desc:bread
2023-05-26 forgot the bread
    expenses              $5
    assets

Show only postings on or after a certain date to an account whose name contains "exp":

$ hledger register date:2023-05-26.. exp
2023-05-26 forgot the bread     expenses                        $5            $5

Show accounts and their balances with "hledger balance"

The third of hledger's three core reporting commands is balance. Use it to list all the accounts posted to, and their balance. You should see account balances agreeing with the final running total in the register reports above:

$ hledger balance
                $485  assets
               $-500  equity:opening balances
                 $15  expenses
--------------------
                   0  

The overall total of these balances is also shown. As with other reports, you can use a query expression to select a subset of the data to report on. Eg:

$ hledger balance assets
                $485  assets
--------------------
                $485  

balance shows balance changes by default

Here's a balance report based only on the postings dated 2023-05-26:

$ hledger balance date:2023-05-26
                 $-5  assets
                  $5  expenses
--------------------
                   0

As you can see from this, balance does not necessarily report real-world account balances; more precisely it shows the sum of the postings you have selected to report on, ie the balance change caused by those postings. When you select a subset of postings, this may be different from the real historical account balance.

Use register to troubleshoot a balance report

If you're not sure where a balance report number is coming from those, run a register report with the same arguments, and the account name, to see the individual postings in that account. Eg for assets above, we can see that the date: query caused the earlier postings to be excluded:

$ hledger register date:2023-05-26 assets
2023-05-26 forgot the bread     assets                         $-5           $-5

Use -H to show historical end balances

Adding the -H/--historical flag to the balance report above makes it show historical balances, instead of balance changes. Historical balances include the effect of postings before the report start date (which otherwise would be excluded here by date:2023-05-26):

$ hledger balance date:2023-05-26 -H
                $485  assets
               $-500  equity:opening balances
                 $15  expenses
--------------------
                   0  

-H works with register reports too (note the running total is now a running historical balance):

$ hledger register date:2023-05-26 assets -H
2023-05-26 forgot the bread     assets                         $-5          $485

-H is useful when you have specified a report start date to limit the report size, but you still want to see real world balances.

Easier reports with "hledger bs" and "hledger is"

hledger's bs/balancesheet and is/incomestatement reports are easier alternatives to the balance command. They correspond to a standard Balance Sheet and an Income Statement (Profit and Loss report) respectively. They will automatically select the proper accounts (though you may need to declare account types to enable this), and bs uses -H by default. Also, they show normal liability, equity and revenue amounts as positive numbers, unlike balance which shows those as negative numbers.

Eg:

$ hledger bs
Balance Sheet 2023-05-26

             || 2023-05-26 
=============++============
 Assets      ||            
-------------++------------
 assets      ||       $485 
-------------++------------
             ||       $485 
=============++============
 Liabilities ||            
-------------++------------
-------------++------------
             ||            
=============++============
 Net:        ||       $485 
$ hledger is
Income Statement 2023-05-01..2023-05-26

          || 2023-05-01..2023-05-26 
==========++========================
 Revenues ||                        
----------++------------------------
----------++------------------------
          ||                        
==========++========================
 Expenses ||                        
----------++------------------------
 expenses ||                    $15 
----------++------------------------
          ||                    $15 
==========++========================
 Net:     ||                   $-15 

Review

You have learned:

  • a simple plain text notation for recording financial transactions, used by hledger and Ledger
  • what is the journal file, where it is, and how to get statistics on it with hledger stats
  • how to record new transactions using hledger add
  • how to record transactions by editing the journal file
  • what the journal entry for a purchase looks like
  • how to detect some common errors, by eye or with hledger
  • how hledger selects data with queries, and how to select by account, description, or date
  • how to list transactions with hledger print
  • how to list postings and see an account's balance over time with hledger register
  • how to list accounts and their balance change or real-world balance, with hledger balance
  • how to produce standard reports more easily with hledger bs and hledger is

Tutorial: hledger-ui

hledger-ui is hledger's TUI (a full-window terminal interface). It's fast, efficient, and a bit more featureful than the web UI. One limitation: on Microsoft Windows, it can only be installed inside WSL. Screenshots below!

Check your installation

Open a terminal or command prompt and check your hledger-ui version. It should be reasonably up to date. This doc was last tested with:

$ hledger-ui --version
hledger-ui 1.17.1.1

If this fails, check Installing for install and setup tips.

Start hledger-ui

Normally, you start hledger-ui by running hledger-ui in a terminal. Any standard terminal app will do, but not a command prompt that doesn't allow cursor positioning (such as an emacs shell buffer). (In that case you would see garbage output like ^[(B^[(B──────────── tmp.journal account balances..., and you would have to press q, RETURN to exit.)

For this tutorial, we'll specify a new temporary journal file, to suit the steps below and avoid disturbing any existing data:

$ echo > tmp.journal
$ hledger-ui -f tmp.journal

A quick tour

With an empty journal, there's not much to see. But we can see: the name of the journal file (tmp.journal) and the screen (account balances) at the top:

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

This bare-bones data entry UI is hledger's add command, also introduced in Easy workflow #1: hledger add.

Here, we'll use it to enter a few transactions, similar to the quick start example. After each prompt, type the value shown in white and press RETURN.

Note when we get to Amount 2, hledger guesses that this is a two-posting transaction and as a default value offers the amount that would balance the transaction ($-1234):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Let's filter for accounts with "checking" in their name. Type checking and press RETURN:

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

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

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

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

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

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

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

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

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

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

While the help dialog is open, you can press p, m or i to see the hledger-ui user manual in several offline formats (useful if you can't access the web version).

The formats are: plain text (p), using your $PAGER for scrolling if possible:

Unix man (m):

or GNU info (i):

Press q to return to hledger-ui.

Tutorial: hledger-web

hledger-web is hledger's web browser-based UI. It's probably the easiest way to get started with hledger. Eg on windows, you can download and unpack hledger.zip and double click on hledger-web.exe. Screenshots below!

Check your installation

Open a terminal or command prompt and check your hledger-web version. It should be reasonably up to date. This doc was last tested with:

$ hledger-web --version
hledger-web 1.17.1

If this fails, check Installing for install and setup tips.

Start hledger-web

Normally, you start hledger-web by running hledger-web in a terminal, with no arguments. Browsing to the executable file and double-clicking on it can also work. Normally, this will start the web app, making it accessible only from your local machine.

For this tutorial, to follow the steps/screenshots below and avoid disturbing any existing data, we'll start hledger-web with a new temporary journal file:

$ mkdir tmp
$ echo > tmp/.hledger.journal
$ hledger-web -f tmp/.hledger.journal

It will print a startup message and keep running, logging any web requests received:

22/Mar/2020:17:34:19 -0700 [Info#yesod-core] Application launched @(yesod-core-1.6.17.2-1LbNd7zV6uz551mHDWJO3b:Yesod.Core.Dispatch src/Yesod/Core/Dispatch.hs:163:11)
Serving web UI and API on 127.0.0.1:5000 with base url http://127.0.0.1:5000
This server will exit after 2m with no browser windows open (or press ctrl-c)
Opening web browser...
127.0.0.1 - - [22/Mar/2020:17:34:21 -0700] "GET / HTTP/1.1" 303 0 "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0"
127.0.0.1 - - [22/Mar/2020:17:34:21 -0700] "GET /journal HTTP/1.1" 200 - "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0"
127.0.0.1 - - [22/Mar/2020:17:34:23 -0700] "GET /static/js/typeahead.bundle.min.js HTTP/1.1" 200 - "http://127.0.0.1:5000/journal" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0"
...

You can leave this terminal in the background, or minimised, but don't quit it (unless you want the web app to quit).

And, your web browser should open, showing the UI. If not, open it yourself and browse to the url shown, ie http://127.0.0.1:5000 (and let us know).

A quick tour

Here's the "home" screen, showing the empty default journal. Or if your journal already contained transactions, you'll see them listed. "General Journal" means the list of transactions, basically.

Let's hide the sidebar, for a simpler UI. Pressing the s key should do it (if not, try again after clicking somewhere on the page, and let us know):

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

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

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

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

Finish the transaction, and press RETURN:

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

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

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

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

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

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

Click the x button or click Journal or press j to see all data again.

You can see quick help by pressing ?, or clicking the ? button near the search field. More help is available in the hledger-web and other manuals, of course:

That's mostly it! Although there are basically just two screens, with hledger's full query language available (and with the general flags available on the command line), you can do a surprising amount with hledger-web. You can also change permissions to enable edit/upload/download access.

Videos, Talks

hledger-related videos:

Audio only:

See also:

Visualising discussion 2023

Some notes excerpted from https://www.reddit.com/r/plaintextaccounting/comments/121ka8m/how_do_you_visualize_drill_down_your_financial.

Allaman

...tracking my financial data on a very fine-grained level. My only concern is, that I treat this only as a “data sink” and I do not reason over my rather sophisticated data I gathered over the years. One reason is, that I am missing an accessible approach to visualizing my data without much effort. Especially, I miss a method to drill down or compare certain categories / timeframes.

I am aware of subcommands like reg and bal or plotting with, but it always felt cumbersome for me to explore my data this way. And I say this as a “shell power user”.

Can you please share your approach to interactively visualizing, exploring and analyzing your data?

gumnos

I tend to use ledger csv to export subsets of interest to a CSV file and then can import that into a spreadsheet (whether that's Excel, Libre Office, or Gnumeric or even gnuplot) to get a more visual representation.

I'm usually most interested in trend-lines of how much certain costs are growing. I just did one for our grocery-spend, confirming that our previously-fairly-consistent costs had gone up notably in the last year.

I've also occasionally done the ledger csv export and imported it into a sqlitedatabase so I can run queries across it more easily. I do complex SQL as part of $DAYJOBso I tend to query with it naturally, unlike ledger/hledger command-line querying syntax which hurts my brain and I have to consult docs

taviso

I just use a Makefile, make spending in my ledger directory generates pretty graphs and tables.

It works pretty well for me, there's just a one-time setup of customizing the report to your liking. If you use xterm, you can generate graphs with sixels and see them directly in the terminal (e.g. set terminal sixel in gnuplot).

sm

I have exactly this problem (and I have been doing this since 2006!). Thanks for describing it so well. Hopefully I am paraphrasing it correctly: 

How to achieve more big-picture clarity and actionable insight from our hledger(/PTA) data with less effort ?

I would really love if we collectively can make some progress on this.

Speaking for myself:

Things tried

  • hledger-chart, bitrotted old charting prototype
  • various third party charting tools on hledger CSV output
  • a big org babel outline with many embedded reports, updatable/viewable in emacs or exportable as markdown/HTML (notebook interface for reports, AKA "reporter's notebook")
  • nightly/weekly reports generated and emailed by cron

Things I currently do

  • a flurry of incremental CLI reporting when I get motivated to find out specific things
  • personalised scripts (mostly using bash) to memoise useful reports and reduce effort
  • an always-running time tracking status report in a drop-down terminal
  • use hledger-ui for fast browsing with drill-down and period stepping (SHIFT-DOWN/UP/LEFT/RIGHT)

Existing things that might help

Other things that might help

  • built in easy charts that display in the CLI, TUI, or WUI without extra setup
  • more / more useful built in financial reports, beyond bs / is
  • cloning the reports (at least) of Fava, Finances 2, or Prudent
  • better docs, eg a complete practical reporting guide (hledger.org and/or PTA wiki)
  • screencasts, videos, demos
  • recurring news post / blogs highlighting contributed real examples/demos
  • ruthless focus, pruning, creative destruction
  • standardisation
  • attracting more devs, writers, power users, accounting/finance know-how
  • prioritisation, funding
  • tabular reports in hledger-ui, hledger-web
  • a more agile/rich GUI app built with flutter or some such

Some missing reports/charts

  • income/expense bar chart
  • asset/liability balance line chart
  • conventional cashflow report
  • built in lots report
  • built in easy investment performance reports
  • invoice aging report

Some obstacles

  • tool discovery, installation, setup, learnability, usability, reliability
  • generality - spanning enough needs, skill levels, platforms, data formats to attract a dev/userbase > 1
  • technical debt
  • bookkeeping debt
  • information debt, docs debt
  • complexity management, cognitive overload, maintaining working memory
  • discipline, consistency, long term focus

edk99

For text report, create a directory with multiple "args" file. Then a script to generate all the reports in 1 pdf file. You shouldn't ask for a specific report, let all be generated and see those you are interested. You set it once and just generate from time to time.

For charting, I use superset. It is a good solution if you have a server, but a bit difficult to install. You can use hledger2psql to convert the journal to a database and you can use the docker-compose file included to install with one command.

If you don't have or don't like or don't want to have a server visualization platform installed as Superset, you can use hledger-plot from hledger-utils. It is easier to install and you don't need a server, but a bit more complicated to create charts in my opinion.

phblj

coming from the parallel universe of beancount... Active analysis is a pain. You can get the data, but there aren't good built-in tools for exploration. That said, when I do have a particular question I'm trying to answer I try to spend the extra time to build it into a fava extension. After a few years I have a set of commonly-used analyses that are at least a jumping off point for whatever I'm interested in looking into next.

ananthakumaran

I ended up building a web GUI on top of the ledger. I reduced the scope to a single country to better fit my personal usage. https://github.com/ananthakumaran/paisa

[hledger and Paisa]

ydant

I use beancount, so Fava is my primary browsing interface, but it definitely has a lot of room for improvement. I think it's also good to have some canned reports you can quickly look at all at once, rather than having to navigate around a UI.

So the main thing I do is have a "family dashboard" Google Sheet for the key data-points my wife wants to see / that we regularly discuss.

It's really easy to write scripts to import data into a Google Sheet, and as long as you structure your formulas, pivot tables, and charts correctly (using open-ended ranges to account for more data being added by future imports of data), you can dump new data in, and have all of the summaries update automatically.

This approach is basically what Tiller does, as a paid service, although I'm proposing just putting high level data into the Google Sheet (e.g. category expenses, daily/monthly/weekly snapshots, not individual transactions).

Allaman

I wrote a blog post describing my approach to this topic.

[export to SQLite and build adhoc charts with Metabase]

hledger and Beancount

Tips for co-using/converting/switching Beancount and hledger.

See also:

Beancount's journal format is different enough from hledger's that one tool can't directly read the other's files, currently.

To convert between the formats, use beancount2ledger and ledger2beancount.

hledger and dsq/DataStation

https://github.com/multiprocessio/datastation GUI for reporting on CSV and other tabular file formats
https://github.com/multiprocessio/dsq command line spinoff

dsq makes CSV and some other things queryable with SQL, as if it was an sqlite database. (It is powered by sqlite, with additional functions from the go sqlite lib). So combined with CSV output from hledger print or hledger register it's an instant new sqlite-like query language. Some examples with dsq 0.20.1:

A quick one-line query on postings:

hledger print -O csv | dsq -s csv -n -p  "select * from {} where date>'2022-06-15' and abs(amount) > 500"

-n interprets numeric fields more accurately as numbers. -p shows pretty text output (otherwise it shows json and runs faster).

If reading one file, you can enable caching with -C, giving a speedup when the file has not changed:

hledger print -o 2022.csv
time dsq 2022.csv -C -n -p "select count(*) from {}"
time dsq 2022.csv -C -n -p "select count(*) from {}"

-i gives an interactive REPL, avoiding reparsing files between commands, for another speedup:

$ dsq 2022.csv -C -n -p -i
dsq> select count(distinct date) from {}
+----------------------+
| count(distinct date) |
+----------------------+
|                  130 |
+----------------------+
(1 row)
dsq> select description,amount from {} order by date desc limit 1
+--------+-------------+
| amount | description |
+--------+-------------+
|    3.5 | chipper     |
+--------+-------------+
(1 row)
dsq>

Fields are sorted alphabetically, for now.

The CLI has a few issues you might run into - combining flags, multiple files, cache initialisation, crashing on empty results...

With no query, dsq outputs JSON (and piping through jq will prettify it). So here's a way to export transactions as human-readable JSON that is simpler than hledger's -O json output:

$ hledger print -O csv | dsq -s csv -n | jq -S
[
  {
    "date2": "",
    "amount": -1,
    "txnidx": 1,
    "credit": 1,
    "description": "income",
    "status": "",
    "code": "",
    "account": "income:salary",
    "date": "2008-01-01",
    "commodity": "$",
    "debit": "",
    "posting-status": "",
    "posting-comment": "",
    "comment": ""
  },
...

hledger and just

https://github.com/casey/just is a cross platform task runner - like make, but easier and more suitable for running commands (and more cross platform). It is a nice tool for creating (and remembering!) financial reports and scripts.

Define commands (AKA "recipes") in a justfile in your finance directory. Each recipe can be a single command line, a shell script, or any other kind of shebang script. Here's just's manual.

We provide an example Scripts > justfile, but of course you can customise this with your own reports or start fresh with just --init. Then you can

$ just -lu         # list your commands, with descriptions, in original order
$ just CMD [ARGS]  # run a command, optionally with extra arguments
$ just --choose    # pick a command interactively
$ alias j=just     # use an easier-to-type alias

Our justfile's commands pass any extra arguments to hledger, but this does not work for arguments containing spaces, so avoid those:

$ just x food -p 'last month'   # bad
$ just x food -plastmonth       # good

Interactive choosing

just --choose will let you choose a command interactively, by default using fzf and showing a preview of the system command(s) that will be run.

You can configure a different chooser with --chooser or JUST_CHOOSER (Just > Selecting Recipes to Run With an Interactive Chooser). Eg on mac, this shows a GUI chooser:

$ brew install choose-gui
$ export JUST_CHOOSER=choose
$ just --choose

Showing report output in the preview pane

This is great for selecting and viewing reports. Careful - it will run every command you select! So be sure your commands are safe and reasonably quick to run (eg hledger reports). Then:

$ export JUST_CHOOSER="fzf --preview='just {}'"
$ just --choose

With bkt, you can cache these outputs, making slow reports display instantly (here's more on --ttl and --stale and using bkt with fzf):

$ export JUST_CHOOSER="fzf --preview='bkt --ttl=15m --stale=15s -- just {}'"
$ just --choose

just view

The example justfile has a view command with the above configuration built in. It uses fzf and bkt (and ignores JUST_CHOOSER). It takes one dummy argument, and optional additional fzf arguments:

$ just view -
$ just view - --black

There is also a pick command which does not run commands while selecting. It uses JUST_CHOOSER, or fzf with the command source as preview. view uses fzf's --reverse flag; you could make pick do the same like so:

$ export JUST_CHOOSER="fzf --preview='-- just --show {}' --reverse"

Excluding commands from the chooser

If you want preview the output of some commands but not of others (eg, because they have side effects), you could move those commands to a separate just file.

Or, you can make sure they are declared with a required argument, adding a dummy argument if necessary (as in the example file). Commands with required arguments are always excluded from the chooser. (Note this excludes them from the chooser entirely, not just from previewing, but they can be run from the command line, writing - for the dummy argument.)

hledger and GnuCash

Tips for co-using/converting/switching GnuCash and hledger.

Relevant data conversion tools include:

Related discussion:

hledger and Ledger

hledger was inspired by the app that pioneered plain text accounting: Ledger (https://ledger-cli.org). This page describes differences between them, and a little history.

If you are a Ledger user trying to use hledger with your data, feel free to skip ahead to Interoperating tips. And please let me know your experience in the #hledger or #plaintextaccounting chats. Related: #1962.

Differences

10000 foot view

How is hledger different from Ledger ? First, the high-order differences:

  • hledger is actively maintained (since 2008)
  • hledger focusses strongly on UX, reliability, and real-world practicality
  • hledger is written in Haskell, which helps with correctness and maintainability
  • hledger tries to reimplement Ledger's best parts in more depth, with improved functionality and robustness.

Compared to Ledger, hledger has

  • a complete and accurate manual
  • standard "financial statement" reports
  • multi-column reports
  • an easier query syntax
  • better depth limiting
  • a battle-tested CSV/SSV/TSV import system
  • and comes with multiple officially-supported user interfaces (CLI, console, TUI, WUI).

Compared to hledger, Ledger has

  • assisted lot tracking for investment transactions
  • more support for embedding small programs in your data to get custom behaviour (value expressions, maybe python expressions ?)
  • smaller executables.

See also:

Features

Over time, features have propagated both ways. Here is a presentation of hledger features and here is a feature comparison as of 2022 (updates welcome):

hledgerLedger
Common features:
journal formatYY
csv formatYY
timeclock formatYY
multiple commoditiesYY
conversion prices and cost reportingYY
market prices and value reportingYY
virtual (unbalanced) postingsYY
automated postingsYY
periodic transactionsYY
budget reportingYY
capital gains reportingYY
report filtering with flags and query argumentsYY
basic output format customisationYY
print, register, balance commandsYY
Features in Ledger only:
automatic revaluation transactions (--revalued)Y
lot reporting (--lots)Y
embedded programming language (value expressions)Y
embedded python snippets / python APIY
probably miscellaneous other things...Y
Features in hledger only:
international number formatsY
timedot formatY
multi-period balance reportsY
account typesY
activity commandY
add commandY
balancesheet commandY
cashflow commandY
check commandY
close commandY
descriptions commandY
diff commandY
files commandY
iadd commandY
import commandY
incomestatement commandY
irr commandY
interest commandY
notes commandY
prices commandY
rewrite commandY
ui commandY
web commandY

Performance

Traditionally, Ledger was faster than hledger with large files (eg >5k transactions). (Many people record about 1-2k transactions per year.) Ledger's extra speed came partly from providing fewer guarantees, eg Ledger's balance assertions/assignments are not date-aware.

Since about 2021 the performance gap seems to me to have closed or reversed, with hledger outperforming Ledger in some cases, including on large files.

In 2022, hledger ~1.25 compiled natively on a macbook air m1 processed 25k transactions per second:

$ hledger --version
hledger 1.24.99.2-gba5b0e93f-20220205, mac-aarch64
$ make throughput
date: Tue Feb 8 11:03:50 HST 2022
system: Darwin slate.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_ARM64_T8101 arm64
executable: hledger
version: hledger 1.24.99.2-gba5b0e93f-20220205, mac-aarch64
  1000 txns: Run time (throughput)    : 0.07s (15308 txns/s)
  2000 txns: Run time (throughput)    : 0.09s (21121 txns/s)
  3000 txns: Run time (throughput)    : 0.13s (23648 txns/s)
  4000 txns: Run time (throughput)    : 0.17s (23226 txns/s)
  5000 txns: Run time (throughput)    : 0.21s (23647 txns/s)
  6000 txns: Run time (throughput)    : 0.24s (24784 txns/s)
  7000 txns: Run time (throughput)    : 0.29s (24166 txns/s)
  8000 txns: Run time (throughput)    : 0.33s (24450 txns/s)
  9000 txns: Run time (throughput)    : 0.35s (25516 txns/s)
 10000 txns: Run time (throughput)    : 0.41s (24226 txns/s)
100000 txns: Run time (throughput)    : 4.32s (23158 txns/s)
Tue Feb  8 11:03:57 HST 2022

More recent hledger versions run a bit slower than this, but on my mac they often run faster and in less memory than Ledger, including with large files. See #2153 for the most recent benchmarking, eg 2153#issuecomment-1912942305. (Avoid hledger versions 1.29-1.32.2, which were affected by this performance bug.)

More independent benchmarking is needed - please help if you can.

Command line interface

  • hledger does not require a space between command-line flags and their values, eg -fFILE works as well as -f FILE

  • hledger uses --ignore-assertions/-I to disable balance assertions. Ledger uses --permissive for that, and uses -I as the short form of --prices.

  • hledger's -x/--explicit flag makes print show all amounts; Ledger's --explicit flag does something else.

  • hledger's and Ledger's -H/--historical flags are unrelated. hledger's -H makes register and balance-like commands include balances from before the report start date, instead of starting at zero:

    hledger register --help:
    -H --historical           show historical running total/balance
                              (includes postings before report start date)
    
    hledger balance --help:
    -H --historical           show historical ending balance in each period
                              (includes postings before report start date)
    

    Whereas Ledger's -H changes the valuation date used by -V/-X:

    ledger --help:
    --historical (-H)
                              Value commodities at the time of their acquisition.
    
  • hledger's query language is a little less powerful than Ledger's, simpler, and easier to remember. It uses google-like prefixes, such as desc:, payee:, amt:, and not:. Multiple query terms are combined using fixed AND/OR rules. We don't yet support full boolean expressions, so some more advanced queries require two invocations of hledger in a pipe, eg: hledger print QUERY1 | hledger -f- reg QUERY2

  • hledger provides more short flags (-b, -e, -p, -D, -W, -M, -Q, -Y) and the date: query argument for setting report period and interval, and all of these combine nicely.

  • hledger cleans up some old semantic confusion around what "uncleared" means:

    • hledger renames Ledger's "uncleared" status (ie, when the status field is empty) to "unmarked", and the --uncleared/-U flag to --unmarked/-U
    • hledger uses -P as the short form of --pending. Ledger uses -P for grouping by payee.
    • each of hledger's --unmarked/-U, --pending/-P, --cleared/-C flags match only that single status. To match more than one status, the flags can be combined.

    So the hledger equivalent of Ledger's -U flag ("match uncleared") is -UP ("match unmarked or pending").

Journal format

hledger's journal file format is very similar to Ledger's. Some syntactic forms (eg hledger comments vs Ledger comments, or balance assertions) can be interpreted in slightly different ways. A small number of Ledger's syntactic forms are ignored (lot notation) or rejected (value expressions). With some care to restrict yourself to compatible features, or to keep non-compatible features in separate files, it's possible to keep a journal file that works with both hledger and Ledger simultaneously. See also #1752.

Here is a detailed list of Ledger's file format features, from the Ledger manual as of 2022-12, and their status in hledger 1.28, hledger dev, and intended (Yes / Ignored / No).

Supported in hledger ?1.28devNotesGoal
Transactions
5.1 Basic formatYY
5.2 Eliding amountsYY
5.3 Auxiliary datesYY
5.4 CodesYY
5.5 Transaction stateYY
5.6 Transaction notesYY
5.7 MetadataYY
5.7.1 Metadata tagsYYformat is TAG1:, TAG2: not :TAG1:TAG2:
5.7.1.1 Payee metadata tagNN
5.7.2 Metadata valuesYYvalues are terminated by comma, can have multiple tag/values on one line
5.7.3 Typed metadataNNdate:/date2: values are checked as dates, all other tag values are strings
5.8 Virtual postingsYY
5.9 Expression amountsNN
5.10 Balance verificationYY
5.10.1 Balance assertionsYY
5.10.1.1 Special assertion value 0YY
5.10.2 Balance assignmentsYY
5.10.3 Resetting a balanceYY
5.10.4 Balancing transactionsYY
5.11 Posting costYY
5.12 Explicit posting costsYY
5.12.1 Primary and secondary commoditiesNN
5.13 Posting cost expressionsNN
5.14 Total posting costsYY
5.15 Virtual posting costsIIthe parentheses have no effect
5.16 Commodity pricesIIcost basis is not tracked separately from costY
5.16.1 Total commodity pricesIILedger lot notation is ignored, but transactions may fail to balance as a resultY
5.17 Prices versus costsNN
5.18 Fixated prices and costsII
5.19 Lot datesIIY
5.20 Lot notesNIY
5.21 Lot value expressionsNN
5.22 Automated TransactionsYY
5.22.1 Amount multipliersYYdifferent syntax
5.22.2 Accessing the matching posting’s amountNN
5.22.3 Referring to the matching posting’s accountNN
5.22.4 Applying metadata to every matched postingYY
5.22.5 Applying metadata to the generated postingYY
5.22.6 State flagsYY
5.22.7 Effective DatesYYsame as Auxiliary Dates
5.22.8 Periodic TransactionsYY
((Amount valuation expressions))NI
Directives link
P historical (market) pricesYY
= An automated transaction.YY
~ A periodic transaction.YYcertain period expressions can only start on an interval boundary (fixed in dev)
; # % * | comment linesYYbut not % or |
! or @ as a directive prefixnot @Y
account pre-declare account namesYY
account subdirectivesIII/Y
apply account set a default parent accountYY
apply fixed set fixated pricesNI
apply tag assign a tag to transactionsNI
alias rewrite account namesYY
assert error if a value expression failsNIuse hledger check or hledger-check-fancyassertions
bucket/A set a default balancing accountNI
capture replace accounts matched by regex with anotherNIcan be emulated with regex alias
check warn if a value expression failsNIuse hledger check or hledger-check-fancyassertions
comment start multi-line commentsYY
commodity pre-declare commoditiesYY
commodity subdirectivesNIall but format are ignoredI/Y
define define value expressions for future useNI
end/end apply shorthand for ending most recent apply blockNNY
end apply accountYY
end apply fixedNI
end apply tagNI
end apply yearNIY
end tagNI
eval/expr evaluate a value expressionNI
include include another fileYY
payee pre-declare payee namesYY
payee subdirectivesNII/Y
python embed python in journalNI
tag pre-declare tag namesIIY
tag subdirectivesNI
test, a synonym for commentNN
value EXPR set a default valuation function for all commoditiesNI
Y/year/apply year set the year for year-less datesonly YY
N COMM ignore pricing information for a commodityII
D AMT set a default commodity and its formatYY
C AMT1 = AMT2 declare a commodity equivalencyNII/Y
I, i, O, o, b, h timeclock entries in journalNNtimeclock data must be in a separate file (can be included)
--command-line-flags in journalNI

Decoding errors

hledger, like most Haskell programs, exits with a confusing error message if it sees non-ascii data and the system locale is not configured to decode UTF-8. If your data contains non-ascii characters and hledger gives an error such as "invalid byte sequence", "mkTextEncoding: invalid argument" or similar, you must configure your locale.

Tabs and spaces

In places which normally require two or more spaces (or tabs), eg between account name and amount, ledger will also accept a single tab character. But hledger always requires two or more spaces or tabs (ensuring a visually distinct gap). So you might need to add a space in such cases.

Decimal mark

Ledger parses 1,000 as 1000, but hledger parses it as 1, by default (see hledger > Decimal marks).

To prevent any undetected disagreements, use commodity directives or decimal-mark directives to disambiguate the decimal mark character during parsing.

Balancing precision

Ledger and hledger can occasionally disagree on whether a transaction is balanced. In this journal, $'s precision (number of decimal places) is 2 in txn1, 4 in txn2, and 4 globally:

2022-01-01 txn1
    expenses                                 AAA 989.02 @ $1.123456  ; $1111.12045312
    checking                                  $-1111.12

2022-01-02 txn2
    expenses                                      $0.1234
    checking

Ledger checks transaction balancedness using local precisions only, so it balances with precision 2, and accepts txn1's $-0.00045312 imbalance.

hledger checks transaction balancedness using global precisions, so it balances with precision 4, and rejects txn1's imbalance. To read these entries with hledger, you have to limit $'s global precision, by adding -c '\$0.00' to the command (easiest when piping) or commodity $0.00 to the file (more permanent, when creating a new file).

More: #1964

Balance assertions / assignments

Ledger calculates balance assignments and checks balance assertions simply in the order they were parsed. hledger calculates balance assignments and checks balance assertions in date order and then (for postings on the same date) parse order. This ensures correct, reliable behaviour independent of the ordering of journal entries and files.

hledger correctly handles multiple balance assignments/assertions within a single transaction.

Ledger rejects the following balance assertion, as if (a) and a were different accounts; hledger does not.

2023-01-01
  (a)  1

2023-01-02
  a    1 = 2
  b

In addition to =, hledger supports several other kinds of balance assertion, with syntax ==, =* and ==*. Ledger rejects these.

hledger allows @/@@ cost notation in balance assertion/assignment amounts, ie to the right of the equals sign; Ledger does not.

hledger adds a restriction on balance assignments: it does not allow balance assignments on accounts affected by auto posting rules (since in general this can make balancing the journal impossible).

Directive scope

The region affected by directives, and their behaviour with included files or sibling files, is sometimes different in hledger. This is to ensure robust, predictable behaviour. Here are hledger's Directive effects and Directives and multiple files behaviour. You might need to move directives and/or rearrange your files.

Commodity directives

hledger allows commodity directives with a format subdirective to be written as one line, eg these are equivalent:

commodity JPY
  format 1.00 JPY

commodity 1.00 JPY

hledger's commodity directive currently ignores other subdirectives (eg alias).

hledger's commodity directive always requires a decimal mark in the amount. To display no decimal digits, write the decimal mark at the end:

commodity 1000. JPY

And as mentioned above, hledger assumes a single period or comma is a decimal mark, so when specifying digit group marks, write a decimal mark as well: Eg:

commodity 1,000. JPY

See also: hledger > commodity directive.

Periodic transactions

hledger understands most Ledger periodic transactions, but if you find some variants that are not supported, please report.

When you do specify a custom start date, hledger will start the transactions on that date. Ledger seems to always generate them on the period boundaries.

Amount expressions

hledger does not support value expressions, Ledger's embedded programming language. In particular, parenthesised amount expressions like ($10 / 3) are not supported; these must be converted to explicit amounts. Here are the known ways:

  • Convert each expression manually, eg replace ($10 / 3) with $3.333.

  • Convert each expression with ledger. Eg in emacs, select the parenthesised expression and enter C-u M-| xargs ledger eval (and remove the newline). This might not work in all cases.

  • Convert all expressions with beancount. This is a lossy conversion, but it might be good enough. After installing ledger2beancount, beancount, and beancount2ledger (see #33), try:

    $ ledger2beancount file.ledger > file.beancount
    $ beancount2ledger file.beancount > file.journal
    

Lot notation

hledger currently does not provide automatic lot selection or a --lots report; instead you must track them manually, recording cost basis with @ and using explicit per-lot subaccounts and gain/loss postings (see https://hledger.org/investments.html).

More importantly, hledger ignores Ledger's lot notation, like -5 AAPL {$50.00} [2012/04/10] (Oh my!) @@ $375.00. (Any of {LOTUNITCOST}, {{LOTTOTALCOST}}, {=FIXEDLOTUNITCOST}, {{=FIXEDLOTTOTALCOST}}, [LOTDATE], (LOTNOTE) after a posting amount). This can disrupt transaction balancing, making files unreadable. (#1084) For now the only true workaround is to rewrite such entries to use hledger-style explicit lot notation.

Other differences

  • hledger's input data formats (journal, timeclock, timedot, ...) are separate; you can't mix timeclock entries and journal entries in one file as in Ledger. (Though a journal file can include a timeclock file.) This helps implement more useful error messages.

  • hledger supports international number formats, auto-detecting the decimal mark (comma or period), digit group mark (period, comma, or space) and digit group sizes to use for each commodity. Or, these can be declared explicitly with commodity directives.

  • hledger's default commodity directive (D) sets the commodity to be used for subsequent commodityless amounts, and also sets that commodity's display settings if such an amount is the first seen. Ledger uses D only for commodity display settings and for the entry command.

  • hledger auto postings allow only minimal customisation of the amount (just multiplying the matched amount by a constant), not a full embedded expression language like Ledger. (And we call them "auto" to avoid "automatic" vs "automated" confusion.)

  • In multi-period reports, hledger expands the report start/end dates to span whole periods.

  • hledger's print command always shows both the primary transaction date and any secondary date, in their usual positions. Ledger's print command with --aux-date replaces the primary date with any secondary date.

  • hledger always shows time balances (from timeclock or timedot data) in hours.

  • hledger always splits multi-day time sessions at midnight, to show the per-day amounts. Ledger does this only with the --day-break flag.

  • hledger's CSV/TSV/SSV-reading and import system is more mature and flexible than Ledger's convert command.

  • Ledger can report multiple errors at once; hledger reports only one error at a time.

  • Ledger can also output warnings. hledger does not print warnings; it either succeeds or fails.

  • hledger will complain if transaction or posting comments contain date: or date2: not followed by a valid date tag value.

Interoperating tips

The core of hledger's and Ledger's journal formats is the same, so you can use both tools on the same files, if you are careful to avoid tool-specific features.

When you can't avoid tool-specific syntax, you can put it in separate tool-specific files, and have both of these include a shared common file. (Eg 2023.ledger and 2023.hledger, both including 2023.journal).

A third approach is to do a one-way conversion to a new file, using whatever edits and transformations are necessary, and automate it as much as possible (with sed, perl, Emacs macros, or similar), so you can redo the conversion when needed, perhaps incrementally.

Ledger to hledger

Most Ledger users will have at least some Ledger-specific syntax, so the quickest way to tap into hledger reports may be:

$ ledger print --raw | hledger -f- -I CMD

The print command omits directives. --raw prevents decimal zeroes being added to amounts and disrupting transaction balancing. -I disables checking of balance assertions (if needed). If this works you can do quick reporting like so:

$ ledger print --raw | hledger -f- check       # check for problems
$ ledger print --raw | hledger -f- stats       # show journal statistics
$ ledger print --raw | hledger -f- is -MAS -2  # summarise monthly revenues/expenses
$ ledger print --raw | hledger -f- web         # view journal in hledger-web WUI
$ hledger-ui -f <(ledger print --raw)          # view journal in hledger-ui TUI (works in bash)

Some common problems:

  • hledger does not support Ledger's amount expressions, like ($10 / 3). If you have those, see Amount expressions above.

  • hledger does not support all of Ledger's lot notation, like -5 AAPL {$50.00} [2012/04/10] (Oh my!) @@ $375.00. It can parse it, but will ignore it, so transaction balancing will probably fail. For now the only true workaround is to rewrite such entries to use hledger-style lot notation. See Lot notation above.

See also the other Differences mentioned above.

hledger to Ledger

Currently there's no specific output format for Ledger; use print's standard txt output format.

$ hledger print | ledger --permissive -f - CMD

Ledger requires a space between -f and -. --permissive disables checking of balance assertions (if needed).

Some common problems:

  • hledger's extended balance assertions (=*, ==, ==*) are not supported by Ledger and must be avoided or commented out (eg with sed -E -e 's/(==|=\*)/; \1/').

  • Transactions which hledger considers balanced (using global display precisions) can be considered unbalanced by Ledger (using local display precisions) (see Balancing precision). Try to make those transaction amounts more precise so that they balance in both.

  • hledger print will add a trailing decimal mark to amounts with digit group marks and no decimal digits, to disambiguate them (since 1.31), but Ledger currently does not parse such numbers. You can avoid them by suppressing digit group marks (eg with -c) or by ensuring some decimal digits (eg with --round); see hledger > Trailing decimal marks.

See also the other Differences mentioned above.

History

I (Simon) discovered John Wiegley's Ledger in 2006, and was very happy to find this efficient command-line reporting tool with a transparent data format. Initially, I used it to generate time reports for my job. Before long I wanted some improvements - splitting sessions at day boundaries, reporting in hours, etc.

Meanwhile, John was now busy elsewhere. For a long time the Ledger project remained stalled, with unfixed functionality/documentation bugs and an ever-looming v3 release making life hard for new users and creating friction for community growth. I did what I could to help - reporting bugs, providing support, contributing a domain and website - but I didn't want to invest in learning C++.

I was learning and investing time in Haskell, and I felt Ledger could be perhaps implemented well, and perhaps more effectively in the long run, in this language. I urgently needed a rock solid, hassle-free and enjoyable accounting tool. Also, I wanted a more active project and some way to make progress on the roadbumps and confusion facing other newcomers.

Of course I tried a little shiny-tech salesmanship on John, but couldn't expect him to start over. (At that time he was deeply in the C++ world; nowadays he is a Haskell expert!)

So in 2007 I began experimenting. I built a toy parser in a few different languages, and it was easiest in Haskell. I kept tinkering. Goals included:

  1. to get better at Haskell by building something useful to me
  2. to implement at least the basic core of Ledger, adapted for my needs
  3. to learn how well Haskell could work for real-world applications

And later:

  1. to provide a new highly-compatible implementation of at least the basics of Ledger, useful to others, with a greater focus on ease of use, reliability, documentation and web presence
  2. to experiment with new user interfaces, APIs, etc.

Before too long I had a tool that was useful to me. With Ledger still installed, and by maintaining high compatibility, I now had two implementations which could be compared at times of confusion about functionality or suspected bugs/bookkeeping errors, which was quite valuable.

Later, John returned for a while and finished Ledger version 3, the Ledger project attracted new contributors and maintainers, and incremental improvements resumed. I continued sharing discoveries and design discussions, and we have seen many ideas propagating in both directions. I think having independent but compatible implementations has been quite helpful for troubleshooting, exploring the design space, and growing the community. For a while I ran LedgerTips on twitter.

hledger shared #ledger's IRC channel until 2014, when I created the #hledger channel (now accessible on Libera IRC and Matrix).

In 2016 I set up https://plaintextaccounting.org as a common entry point and information hub.

The further adventures in hledger's development are not yet told, other than in the commit log, issue tracker and mail list, but other contributors joined the project and CREDITS notes some of their work.

hledger and Obsidian

ledger-obsidian

obsidian_hledger

hledger and Paisa

https://github.com/ananthakumaran/paisa is a web app that reads a Ledger (or hledger) file, downloads market prices, and provides sophisticated (read-only) reports and charts (read only). It is a slick, modern, easy-to-install app written in go and typescript and released under AGPL. It is focussed on Indian users but can be useful to others. In 2023 it is the best reporting app available to hledger and Ledger users.

Paisa uses Ledger to read the journal file into its own SQLite database. You must have Ledger installed at least to create the SQLite database initially. After the database is created Paisa doesn't need Ledger, but it is still useful to sync the latest changes from your journal to the db periodically.

Paisa doesn't use any Ledger-specific features however, so it's relatively easy to ensure either that Ledger can read your hledger journal, or (more likely) that it can read a copy made with hledger print.

Tips

If you have trouble making your journal readable by Ledger, look for ideas at Ledger. Eg

  • some transactions that balance in hledger will not balance in Ledger and vice versa. You might need to add more decimal places in cost amounts, or you might need to preserve hledger's commodity declarations
  • costs in balance assertion/assignment amounts can not be read by Ledger and will need to be removed or commented
  • some hledger balance assertions can not be read by Ledger and will need to be modified or commented out.

Paisa will only see accounts whose top level is Assets, Liabilities, Equity, Income or Expense (case insensitive). If you don't use those, you can use print --alias to rewrite them. For other account names special to Paisa, see https://ananthakumaran.in/paisa/accounts.html.

Installing hledger as a fake Ledger

Paisa runs Ledger like this:

ledger -f FILE csv --csv-format "%(quoted(date)),%(quoted(payee)),%(quoted(display_account)),%(quoted(commodity(scrub(display_amount)))),%(quoted(quantity(scrub(display_amount)))),%(quoted(to_int(scrub(market(amount,date) * 100000)))),%(quoted(xact.id))\n"

which prints

2014/01/01 Home purchase
    Assets:House                                1 APT @ 4000000 INR
    Liabilities:Homeloan

as

"2014/01/01","Home purchase","Assets:House","APT","1","400000000000","1"
"2014/01/01","Home purchase","Liabilities:Homeloan","INR","-4000000","-400000000000","1"

hledger print -O csv prints that entry as

"txnidx","date","date2","status","code","description","comment","account","amount","commodity","credit","debit","posting-status","posting-comment"
"1","2014-01-01","","","","Home purchase","","Assets:House","1","APT","","1","",""
"1","2014-01-01","","","","Home purchase","","Liabilities:Homeloan","-4000000.0000","INR","4000000.0000","","",""

Perhaps a "ledger" script could transform our CSV into Ledger's.

Adding hledger support to Paisa

This would be ideal. https://github.com/ananthakumaran/paisa/blob/master/internal/ledger/ledger.go is the place if you would like to work on it. https://github.com/ananthakumaran/paisa/discussions/12

hledger and Postgres

hledger2psql is a tool that exports a hledger journal to a postgres database.

hledger and Quicken/Quickbooks

Tips for co-using/converting/switching Quicken or Quickbooks and hledger.

Relevant data conversion tools include:

hledger and SQLite

With SQLite you can do full relational queries on your hledger data.

Here we export all transactions to a database and run some queries:

$ hledger -f examples/bcexample.hledger print -O sql | sqlite3 bcexample.db
$ sqlite3 bcexample.db -column -header
SQLite version 3.37.0 2021-12-09 01:34:53
Enter ".help" for usage hints.
sqlite> .tables
postings
sqlite> .schema
CREATE TABLE postings(id serial,txnidx int,date1 date,date2 date,status text,code text,description text,comment text,account text,amount numeric,commodity text,credit numeric,debit numeric,posting_status text,posting_comment text);
sqlite> select count(*) from postings;
count(*)
--------
3203
sqlite> select distinct txnidx,description,abs(amount) from postings order by abs(amount) desc limit 5;
txnidx  description                                       abs(amount)
------  ------------------------------------------------  -----------
1031    Allowed contributions for one year                17500
1035    Allowed contributions for one year                17500
1027    Allowed contributions for one year                17000
142     Transfering accumulated savings to other account  5000
954     Hoogle | Payroll                                  4615.38
sqlite> sqlite> select * from postings where txnidx=954;
id  txnidx  date1       date2  status  code  description       comment  account                                     amount    commodity  credit   debit    posting_status  posting_comment
--  ------  ----------  -----  ------  ----  ----------------  -------  ------------------------------------------  --------  ---------  -------  -------  --------------  ---------------
    954     2012-01-05         *             Hoogle | Payroll           Assets:US:BofA:Checking                     1350.6    USD                 1350.6
    954     2012-01-05         *             Hoogle | Payroll           Assets:US:Vanguard:Cash                     1200      USD                 1200
    954     2012-01-05         *             Hoogle | Payroll           Assets:US:Federal:PreTax401k                -1200     IRAUSD     1200
    954     2012-01-05         *             Hoogle | Payroll           Expenses:Taxes:Y2012:US:Federal:PreTax401k  1200      IRAUSD              1200
    954     2012-01-05         *             Hoogle | Payroll           Income:US:Hoogle:Salary                     -4615.38  USD        4615.38
    954     2012-01-05         *             Hoogle | Payroll           Income:US:Hoogle:GroupTermLife              -24.32    USD        24.32
    954     2012-01-05         *             Hoogle | Payroll           Expenses:Health:Life:GroupTermLife          24.32     USD                 24.32
    954     2012-01-05         *             Hoogle | Payroll           Expenses:Health:Dental:Insurance            2.9       USD                 2.9
    954     2012-01-05         *             Hoogle | Payroll           Expenses:Health:Medical:Insurance           27.38     USD                 27.38
    954     2012-01-05         *             Hoogle | Payroll           Expenses:Health:Vision:Insurance            42.3      USD                 42.3
    954     2012-01-05         *             Hoogle | Payroll           Expenses:Taxes:Y2012:US:Medicare            106.62    USD                 106.62
    954     2012-01-05         *             Hoogle | Payroll           Expenses:Taxes:Y2012:US:Federal             1062.92   USD                 1062.92
    954     2012-01-05         *             Hoogle | Payroll           Expenses:Taxes:Y2012:US:State               365.08    USD                 365.08
    954     2012-01-05         *             Hoogle | Payroll           Expenses:Taxes:Y2012:US:CityNYC             174.92    USD                 174.92
    954     2012-01-05         *             Hoogle | Payroll           Expenses:Taxes:Y2012:US:SDI                 1.12      USD                 1.12
    954     2012-01-05         *             Hoogle | Payroll           Expenses:Taxes:Y2012:US:SocSec              281.54    USD                 281.54
    954     2012-01-05         *             Hoogle | Payroll           Assets:US:Hoogle:Vacation                   4.62      VACHR               4.62
    954     2012-01-05         *             Hoogle | Payroll           Income:US:Hoogle:Vacation                   -4.62     VACHR      4.62
sqlite>

You can avoid creating a .db file, always querying the latest journal file instead:

$ (hledger print -O sql; echo "select * from postings where account like 'liabilities%' and amount > 0") | sqlite3 -column -header

Here's a shell function to make that convenient:

$ hq() { (hledger print -O sql; echo "$1") | sqlite3 -column -header; }

Example queries:

$ hq "select distinct(account) from postings order by account"
$ hq "select * from postings where account like '%savings%' and amount > 0"

The txnidx field connects postings belonging to the same transaction. Using this, we can query transactions, and in more complex ways than hledger can. Here's an example where we want to see just the transfers from Checking to Liabilities. This is hard to do accurately with hledger's CLI(1):

$ echo; hq "select * from postings where txnidx in \
  (select txnidx from postings where account regexp 'Liabilities' and amount > 0 and txnidx in \
    (select txnidx from postings where account regexp 'Checking' and amount < 0))"
> > 
id  txnidx  date1       date2  status  code  description                           comment  account                      amount   commodity  credit  debit   posting_status  posting_comment
--  ------  ----------  -----  ------  ----  ------------------------------------  -------  ---------------------------  -------  ---------  ------  ------  --------------  ---------------
    147     2012-01-08         *             Chase:Slate | Paying off credit card           Liabilities:US:Chase:Slate   140.36   USD                140.36                                 
    147     2012-01-08         *             Chase:Slate | Paying off credit card           Assets:US:BofA:Checking      -140.36  USD        140.36                                         
    163     2012-02-11         *             Chase:Slate | Paying off credit card           Liabilities:US:Chase:Slate   725.96   USD                725.96                                 
    163     2012-02-11         *             Chase:Slate | Paying off credit card           Assets:US:BofA:Checking      -725.96  USD        725.96                                         
    177     2012-03-09         *             Chase:Slate | Paying off credit card           Liabilities:US:Chase:Slate   580      USD                580                                    
    177     2012-03-09         *             Chase:Slate | Paying off credit card           Assets:US:BofA:Checking      -580     USD        580                                            
...

(1. Well.. it's not that hard to get a decent result given typical data patterns:

$ hledger -f examples/bcexample.hledger print Checking | hledger -f- areg -w80 Liabilities amt:'>0'
Transactions in Liabilities and subaccounts:
2012-01-08 Chase:Slate | Pay..  Li:US:Ch:Slate          140.36 USD    140.36 USD
2012-02-11 Chase:Slate | Pay..  Li:US:Ch:Slate          725.96 USD    866.32 USD
2012-03-09 Chase:Slate | Pay..  Li:US:Ch:Slate          580.00 USD   1446.32 USD
...

but in general, this is not as robust as the relational query.)

Only hledger's print command outputs SQL. But many hledger reports can produce CSV, and you can also run SQLite on that:

or on hledger's JSON output:

See also hledger and dsq / DataStation.

And Michael Peter: My plain text accounting workflow with hledger. This recommends the following export command to create a useful primary key:

hledger print -O sql | sed 's/id serial/id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL/g' | sqlite3 ledger.db

(#2017)

hledger and Ultorg

Ultorg provides a slick visual UI for relational data. It is an easy and powerful way to visualise and analyse your hledger data in a tabular GUI.

Ultorg goes farther than spreadsheets, with more powerful joins and layout. https://www.ultorg.com/resources > 1st video demonstrates its key features, and the 3rd video is a fascinating demo of analysing Quickbooks data.

Exporting to Ultorg via CSV

This method is perhaps the easiest.

  1. Export a hledger journal as CSV, eg:

    $ hledger -f examples/bcexample.hledger print -o examples/bcexample.csv
    

    or

    $ hledger print -o main.csv
    
  2. Run Ultorg.

  3. Click Add Data Source in the left panel, click Connect to File, and select the CSV file. Press enter to confirm the import settings.

  4. The CSV file appears in the left panel, containing one table. Double-click on the table to open it in the main pane.

  5. Group all the per-transaction columns: CMD/CTRL-click the headings of txnidx, date, status, and description, right-click the headings and choose Custom Group (CMD-I).

  6. Now hide uninteresting columns, eg perhaps Row Number, status: CMD/CTRL-click the headings, right-click -> Hide (CMD-SHIFT-K). (Don't hide txnidx, it seems to help date sorting.)

  7. And sort by date: right-click date heading, Sort Ascending.

Exporting to Ultorg via SQlite

The results are much the same as the above, though you will also have an SQLite database to play with (see hledger and SQLite). This method requires Ultorg >=1.26. Note also ultorg#53.

  1. Export a hledger journal as an Sqlite database. Eg:

    $ hledger -f examples/bcexample.hledger print -O sql | sqlite3 bcexample.db
    

    or

    $ hledger print -O sql | sqlite3 main.db
    
  2. Run Ultorg.

  3. Click Add Data Source in the left panel, click Connect to File, and select the .db file.

  4. The db file appears in the left panel, containing one table. Double-click on the table (Postings) to open it in the main pane.

  5. Group all the per-transaction columns: CMD/CTRL-click the headings of Txnidx, Date 1, Date 2, Status, Code, Description and Comment, right-click the headings and choose Custom Group (CMD-I).

  6. Now hide uninteresting columns, eg perhaps Date2, Status, Code, ID, Posting Status: CMD/CTRL-click their headings, right-click -> Hide (CMD-SHIFT-K). (Don't hide Txnidx, it seems to help date sorting.)

  7. And sort by date: right-click Date 1 heading, Sort Ascending.

Things to try

  1. Click the Amount column, then click the toolbar button that (by default) looks like a bar chart.

  2. Click the toolbar button that (by default) looks like a table and select a different Auto-Layout Type, like Form (Multi-Column).

hledger and You Need A Budget

https://www.ynab.com

Related:

hledgermatic

A relatively simple, automated workflow using current hledger features. This is the kind of setup I use myself as of 2023. The Files layout will give you the gist of it. It is a journal first setup (journal files are primary, CSV files are disposable).

Tools

hledger 1.30+ , just, git (or other version control system), ...

Files

Here are the main files, grouped for clarity. These are unix-style file paths; on Windows, they may be a little different. These filenames are reasonably good for typing, autocompleting, visual grouping, editor configuration, etc. But you might find better ones.

/home/USER/

 finance/                 home of finance files; wherever you wish

  .git/                   most files are tracked with git or other VCS
   hooks/
    pre-commit            runs hledger check -s ordereddates recentassertions

  bin/                    extra scripts/tools for finance work
   bashrc                 extra shell config
   gsheet-csv             get a Google sheet as CSV
   paypaljson             get recent Paypal transactions as JSON
   paypaljson2csv         convert Paypal JSON to CSV

  justfile                commands/scripts for common tasks

  2023.journal            main journal file for 2023 (current year)
  2023accounts.journal    account declarations, included by main journal
  2023prices.journal      market price declarations, included by main

  2022.journal            main journal file for 2022 (etc..)
  2022accounts.journal
  2022prices.journal

  forecast.journal        future transactions / periodic transaction rules

  bi-ichecking.csv.rules  CSV conversion rules for each bank account
  paypal.csv.rules
  wf-pchecking.csv.rules
  wf-bchecking.csv.rules
  wf.rules                common rules included for all wf accounts
  common.rules            common rules included by all

 Downloads/               hledger looks here for CSV files by default;
                          these file names are declared in *.csv.rules
  Checking1.csv           a bank account's recent transactions
  Checking1-1.csv         newer copies saved by web browser
  Checking1-2.csv         the newest; hledger will read this one
  Savings1.csv
  Transactions-2342.csv

Scripts

justfile contains common reporting commands and task scripts, which can be listed and invoked by just1. I alias just to j.

The bin directory is a place to keep additional scripts and tools; it should be added to your shell's PATH.

The scripts and an example justfile can be found at Scripts and add-ons.

When you want to add a custom script, you have a choice:

  • you can define it as a shell alias or function in bashrc
  • you can define it as an executable file in bin
  • you can define it as a script in justfile (or a Makefile, Shake file, or other build tool)

Over time, scripts can proliferate and become a maintenance and memory problem. I recommend using justfile where possible; it is the best combination of ease and power. I am increasingly using it as the starting point for all finance tasks.

Main journal

The main journal file is YEAR.journal. The LEDGER_FILE environment variable should be set to this.

It contains these sections, one after another:

  • commodity declarations - for error checking and to set display styles/precision - recommended
  • tag declarations - if you use tags, for error checking
  • include directives - to include other files. I try to keep files to a minimum, to reduce headaches, but certain things are convenient to keep in separate files, like YEARaccounts.journal and YEARprices.journal.
  • (account aliases - I avoid permanently defined aliases, since they add complexity. But if you need them, define them here so they can affect everything.)
  • transactions - from all accounts, in date order. New transactions can simply be appended at the end.

Accounts

Account declarations are kept in a separate YEARaccounts.journal, included by the main journal, for more convenient reviewing and updating. These provide error checking and declare account types, account display order, account tags and/or comments. New account declarations can be inserted in the correct place, or appended at the end.

Eg:

account assets                   ; type:A
account liabilities              ; type:L
account equity                   ; type:E
account revenues                 ; type:R
account expenses                 ; type:X

account assets:bank              ; type:C
account assets:cash              ; type:C
account equity:conversion        ; type:V

account assets:bank:checking
account assets:bank:savings
# etc...

Business accounts

As a sole proprietor, I like to track my business and my personal life as two separate entities. Legally we are one entity, and there are frequent transactions between them, so it's convenient to keep them in the same journal. But I use a top-level entity prefix to keep the accounts clearly separated. I use two letters for these (less typing, less visual noise and keeps them aligned in reports). So my account declarations look like this:

; business 1
account JS:assets               ; type:A
account JS:liabilities          ; type:L
account JS:equity               ; type:E
account JS:revenues             ; type:R
account JS:expenses             ; type:X
account JS:assets:bank          ; type:C
account JS:assets:cash          ; type:C
account JS:equity:conversion    ; type:V
account JS:assets:bank:checking
account JS:assets:bank:savings
# etc...

; personal
account sm:assets               ; type:A
account sm:liabilities          ; type:L
account sm:equity               ; type:E
account sm:revenues             ; type:R, taxable personal revenues
account sm:income               ; type:R, already-taxed "salary" from JS, non-taxable income
account sm:expenses             ; type:X
account sm:assets:bank          ; type:C
account sm:assets:cash          ; type:C
account sm:equity:conversion    ; type:V
account sm:assets:bank:checking
account sm:assets:bank:savings
# etc...

Prices

Market price declarations are also kept in a separate, included file, YEARprices.journal, in date order. New prices can be appended at the end. Eg:

# ...
P 2023-06-08 00:00:00 £ $1.25161
P 2023-06-08 00:00:00 ¥ $0.00719
P 2023-06-08 00:00:00 € $1.07676
P 2023-06-09 00:00:00 £ $1.25777
P 2023-06-09 00:00:00 ¥ $0.00717
P 2023-06-09 00:00:00 € $1.07713
P 2023-09-04 00:00:00 € $1.08021
# ...

forecast journal

forecast.journal is a place to note expected future transactions, one-time or recurring. I include it in the main journal, and I put only periodic transaction rules (and a few auto posting rules) in it. This means it has no effect until I run reports with the --forecast option.

Or you could put ordinary transaction entries here; in that case you may want to not include it, to keep future events out of reports by default.

CSV rules

Each CSV (or TSV, SSV, ...) data source has a CSV rules file declaring how to convert it to meaningful journal entries. Each rules file declares its corresponding data file's name, so that (eg) hledger import wf-pchecking.csv.rules will automatically look for the latest-numbered Checking1*.csv file in the ~/Downloads folder.

Some rules files are common rules included by the others, eg wf.rules and common.rules.

Downloads

~/Downloads is where my mac web browsers save downloaded CSV files, and it's where the source rule looks for data files by default. If you don't have this folder on your system, you can make it, or use a symbolic link; or you can specify a different folder in your source rules.

When downloading bank CSV files, you don't need to care much about which dates you download; hledger's import system will usually do the right thing. Just make sure to download enough data to cover the period since your last import (eg the last 30 days, last year, or all transactions).

You also don't need to care much about managing downloaded CSV files; your browser will give them unique filenames, and hledger will automatically choose the latest ones. After successful import you can either delete the CSV files, keep them around for a while for troubleshooting, or archive them permanently.

Workflow

In the justfile I have a foo-import script for each data source foo, and the import script runs all of them. So it's

  • download one or more CSVs manually in web browser
  • j import --dry
  • j import
  • load journal in emacs + ledger-mode + flycheck-hledger
  • select new transactions, M-q to align them
  • for each new transaction: review, refine and mark it cleared when appropriate.
  • open flycheck's error list with C-c ! l, jump to and resolve each issue in turn. These will be things like typos, uncategorised postings, mis-ordered dates etc.
  • I also run the recentassertions check, which periodically forces me to add more recent balance assertions for my main accounts. I do this by adding a "reconcile" transaction (using yasnippet, by typing "reconcile" and TAB); filling in the balances hledger expects, to make the errors go away
  • When the journal is again error-free, I check each account's real-world balance against the "reconcile" transaction's asserted balance (or in a balance report or hledger-ui accounts screen), and resolve any disagreements.
  • Finally I commit the changes to journal, rules and scripts. Git's pre-commit hook runs my checks one more time, catching any last-minute errors.

1

just is Like make, but better for this purpose. I recommend trying it, even though it doesn't come with your OS; I say this after years of costly battle with make, shell, and other scripting setups.

hledger

NAME

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

SYNOPSIS

hledger
hledger COMMAND [OPTS] [ARGS]
hledger ADDONCMD -- [OPTS] [ARGS]

DESCRIPTION

hledger is a robust, user-friendly, cross-platform set of programs for tracking money, time, or any other commodity, using double-entry accounting and a simple, editable file format. hledger is inspired by and largely compatible with ledger(1), and largely interconvertible with beancount(1).

This manual is for hledger's command line interface, version 1.33.99. 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
  assets:cash

Transactions are dated movements of money (etc.) between two or more accounts: bank accounts, your wallet, revenue/expense categories, people, etc. You can choose any account names you wish, using : to indicate subaccounts. There must be at least two spaces between account name and amount. Positive amounts are inflow to that account (debit), negatives are outflow from it (credit). (Some reports show revenue, liability and equity account balances as negative numbers as a result; this is normal.)

hledger's add command can help you add transactions, or you can install other data entry UIs like hledger-web or hledger-iadd. For more extensive/efficient changes, use a text editor: Emacs + ledger-mode, VIM + vim-ledger, or VS Code + hledger-vscode are some good choices (see https://hledger.org/editors.html).

To get started, run hledger add and follow the prompts, or save some entries like the above in $HOME/.hledger.journal, then try commands like:

$ hledger print -x
$ hledger aregister assets
$ hledger balance
$ hledger balancesheet
$ hledger incomestatement

Run hledger to list the commands. See also the "Starting a journal file" and "Setting opening balances" sections in PART 5: COMMON TASKS.

PART 1: USER INTERFACE

Input

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

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

Commands

hledger provides various subcommands for getting things done. Most of these commands do not change the journal file; they just read it and output a report. A few commands assist with adding data and file management.

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.

Options

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'

or:

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

or:

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

or:

$ hledger ui cur:\\\\$

If you wondered why four backslashes, perhaps this helps:

unescaped:$
escaped:\$
double-escaped:\\$
triple-escaped:\\\\$

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

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:

-txtcsv/tsvhtmljsonsql
aregisterYYYY
balanceY 1Y 1Y 1,2Y
balancesheetY 1Y 1Y 1Y
balancesheetequityY 1Y 1Y 1Y
cashflowY 1Y 1Y 1Y
incomestatementY 1Y 1Y 1Y
printYYYY
registerYYY
  • 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).

Colour

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.

Box-drawing

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.

Paging

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

Environment

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.

PART 2: DATA FORMATS

Journal

hledger's usual data source is a plain text file containing journal entries in hledger journal format. If you're looking for a quick reference, jump ahead to the journal cheatsheet (or use the table of contents at https://hledger.org/hledger.html).

This file represents an accounting General Journal. The .journal file extension is most often used, though not strictly required. The journal file contains a number of transaction entries, each describing a transfer of money (or any commodity) between two or more named accounts, in a simple format readable by both hledger and humans.

hledger's journal format is compatible with most of Ledger's journal format, but not all of it. The differences and interoperation tips are described at hledger and Ledger. With some care, and by avoiding incompatible features, you can keep your hledger journal readable by Ledger and vice versa. This can useful eg for comparing the behaviour of one app against the other.

You can use hledger without learning any more about this file; just use the add or web or import commands to create and update it.

Many users, though, edit the journal file with a text editor, and track changes with a version control system such as git. Editor 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 ;

comment
Or, lines can be enclosed within "comment" / "end comment".
This is a block of 
commented lines.
end comment

# Some journal entries can have semicolon comments at end of line  ; like this
# Some of them require 2 or more spaces before the semicolon.

###############################################################################

# 2. Directives customise processing or output in some way.
# You don't need any directives to get started.
# But they can add more error checking, or change how things are displayed.
# They begin with a word, letter, or symbol. 
# They are most often placed at the top, before transactions.

account assets             ; Declare valid account names and display order.
account assets:savings     ; A subaccount. This one represents a bank account.
account assets:checking    ; Another. Note, 2+ spaces after the account name.
account assets:receivable  ; Accounting type is inferred from english names,
account passifs            ; or declared with a "type" tag, type:L
account expenses           ; type:X
                           ; A follow-on comment line, indented.
account expenses:rent      ; Expense and revenue categories are also accounts.
                           ; Subaccounts inherit their parent's type.

commodity $0.00         ; Declare valid commodities and their display styles.
commodity 1.000,00 EUR

decimal-mark .          ; The decimal mark used in this file (if ambiguous).

payee Whole Foods       ; Declare a valid payee name.

tag trip                ; Declare a valid tag name.

P 2024-03-01 AAPL $179  ; Declare a market price for AAPL in $ on this date.

include other.journal   ; Include another journal file here.

# Declare a recurring "periodic transaction", for budget/forecast reports
~ monthly  set budget goals  ; <- Note, 2+ spaces before the description.
    (expenses:rent)      $1000
    (expenses:food)       $500

# Declare an auto posting rule, to modify existing transactions in reports
= revenues:consulting
    liabilities:tax:2024:us          *0.25  ; Add a tax liability & expense
    expenses:tax:2024:us            *-0.25  ; for 25% of the revenue.

###############################################################################

# 3. Transactions are what it's all about.
# They are dated events, usually movements of money between 2 or more accounts.
# They begin with a numeric date.
# Here is their basic shape:
#
# DATE DESCRIPTION    ; The transaction's date and optional description.
#   ACCOUNT1  AMOUNT  ; A posting of an amount to/from this account, indented.
#   ACCOUNT2  AMOUNT  ; A second posting, balancing the first.
#   ...               ; More if needed. Amounts must sum to zero.
#                     ; Note, 2+ spaces between account names and amounts.

2024-01-01 opening balances         ; At the start, declare pre-existing balances this way.
    assets:savings          $10000  ; Account names can be anything. lower case is easy to type.
    assets:checking          $1000  ; assets, liabilities, equity, revenues, expenses are common.
    liabilities:credit card  $-500  ; liabilities, equity, revenues balances are usually negative.
    equity:start                    ; One amount can be left blank. $-10500 is inferred here.
                                    ; Some of these accounts we didn't declare above,
                                    ; so -s/--strict would complain.

2024-01-03 ! (12345) pay rent
    ; Additional transaction comment lines, indented.
    ; There can be a ! or * after the date meaning "pending" or "cleared".
    ; There can be a parenthesised (code) after the date/status.
                                    ; Amounts' sign shows direction of flow.
    assets:checking          $-500  ; Minus means removed from this account (credit).
    expenses:rent             $500  ; Plus means added to this account (debit).

; Keeping transactions in date order is optional (but helps error checking).

2024-01-02 Gringott's Bank | withdrawal  ; Description can be PAYEE | NOTE
    assets:bank:gold       -10 gold
    assets:pouch            10 gold

2024-01-02 shopping
    expenses:clothing        1 gold
    expenses:wands           5 gold
    assets:pouch            -6 gold

2024-01-02 receive gift
    revenues:gifts          -3 "Chocolate Frogs"  ; Complex commodity symbols
    assets:pouch             3 "Chocolate Frogs"  ; must be in double quotes.

2024-01-15 buy some shares, in two lots                 ; Cost can be noted.
    assets:investments:2024-01-15     2.0 AAAA @ $1.50  ; @  means per-unit cost
    assets:investments:2024-01-15-02  3.0 AAAA @@ $4    ; @@ means total cost
                      ; ^ Per-lot subaccounts are sometimes useful.
    assets:checking                 $-7

2024-01-15 assert some account balances on this date
    ; Balances can be asserted in any transaction, with =, for extra error checking.
    ; Assertion txns like this one can be made with hledger close --assert --show-costs
    ;
    assets:savings                    $0                   = $10000
    assets:checking                   $0                   =   $493
    assets:bank:gold                   0 gold              =    -10 gold
    assets:pouch                       0 gold              =      4 gold
    assets:pouch                       0 "Chocolate Frogs" =      3 "Chocolate Frogs"
    assets:investments:2024-01-15      0.0 AAAA            =      2.0 AAAA @  $1.50
    assets:investments:2024-01-15-02   0.0 AAAA            =      3.0 AAAA @@ $4
    liabilities:credit card           $0                   =  $-500

2024-02-01 note some event, or a transaction not yet fully entered, on this date
    ; Postings are not required.

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

Comments

Lines in the journal will be ignored if they begin with a hash (#) or a semicolon (;). (See also Other syntax.) hledger will also ignore regions beginning with a comment line and ending with an end comment line (or file end). Here's a suggestion for choosing between them:

  • # for top-level notes
  • ; for commenting out things temporarily
  • comment for quickly commenting large regions (remember it's there, or you might get confused)

Eg:

# a comment line
; another commentline
comment
A multi-line comment block,
continuing until "end comment" directive
or the end of the current file.
end comment

Some hledger entries can have same-line comments attached to them, from ; (semicolon) to end of line. See Transaction comments, Posting comments, and Account comments below.

Transactions

Transactions are the main unit of information in a journal file. They represent events, typically a movement of some quantity of commodities between two or more named accounts.

Each transaction is recorded as a journal entry, beginning with a simple date in column 0. This can be followed by any of the following optional fields, separated by spaces:

  • a status character (empty, !, or *)
  • a code (any short number or text, enclosed in parentheses)
  • a description (any remaining text until end of line or a semicolon)
  • a comment (any remaining text following a semicolon until end of line, and any following indented lines beginning with a semicolon)
  • 0 or more indented posting lines, describing what was transferred and the accounts involved (indented comment lines are also allowed, but not blank lines or non-indented lines).

Here's a simple journal file containing one transaction:

2008/01/01 income
  assets:bank:checking   $1
  income:salary         $-1

Dates

Simple dates

Dates in the journal file use simple dates format: YYYY-MM-DD or YYYY/MM/DD or YYYY.MM.DD, with leading zeros optional. The year may be omitted, in which case it will be inferred from the context: the current transaction, the default year set with a Y directive, or the current date when the command is run. Some examples: 2010-01-31, 2010/01/31, 2010.1.31, 1/31.

(The UI also accepts simple dates, as well as the more flexible smart dates documented in the hledger manual.)

Posting dates

You can give individual postings a different date from their parent transaction, by adding a posting comment containing a tag (see below) like date:DATE. This is probably the best way to control posting dates precisely. Eg in this example the expense should appear in May reports, and the deduction from checking should be reported on 6/1 for easy bank reconciliation:

2015/5/30
    expenses:food     $10  ; food purchased on saturday 5/30
    assets:checking        ; bank cleared it on monday, date:6/1
$ hledger -f t.j register food
2015-05-30                      expenses:food                  $10           $10
$ hledger -f t.j register checking
2015-06-01                      assets:checking               $-10          $-10

DATE should be a simple date; if the year is not specified it will use the year of the transaction's date.
The date: tag must have a valid simple date value if it is present, eg a date: tag with no value is not allowed.

Status

Transactions (or individual postings within a transaction) can have a status mark, which is a single character before the transaction description (or posting account name), separated from it by a space, indicating one of three statuses:

mark  status
 unmarked
!pending
*cleared

When reporting, you can filter by status with the -U/--unmarked, -P/--pending, and -C/--cleared flags (and you can combine these, eg -UP to match all except cleared things). Or you can use the status:, status:!, and status:* queries, or the U, P, C keys in hledger-ui.

(Note: in Ledger the "unmarked" state is called "uncleared"; in hledger we renamed it to "unmarked" for semantic clarity.)

Status marks are optional, but can be helpful eg for reconciling with real-world accounts. Some editor modes provide highlighting and shortcuts for working with status. Eg in Emacs ledger-mode, you can toggle transaction status with C-c C-e, or posting status with C-c C-c.

What "uncleared", "pending", and "cleared" actually mean is up to you. Here's one suggestion:

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

Code

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.

Description

After the date, status mark and/or code fields, the rest of the line (or until a comment is begun with ;) is the transaction's description. Here you can describe the transaction (called the "narration" in traditional bookkeeping), or you can record a payee/payer name, or you can leave it empty.

Transaction descriptions show up in print output and in register reports, and can be listed with the descriptions command.

You can query by description with desc:DESCREGEX, or pivot on description with --pivot desc.

Payee and note

Sometimes people want a dedicated payee/payer field that can be queried and checked more strictly. If you want that, you can write a | (pipe) character in the description. This divides it into a "payee" field on the left, and a "note" field on the right. (Either can be empty.)

You can query these with payee:PAYEEREGEX and note:NOTEREGEX, list their values with the payees and notes commands, or pivot on payee or note.

Note: in transactions with no | character, description, payee, and note all have the same value. Once a | is added, they become distinct. (If you'd like to change this behaviour, please propose it on the mail list.)

If you want more strict error checking, you can declare the valid payee names with payee directives, and then enforce these with hledger check payees. (Note: because of the above, for this you'll need to ensure every transaction description contains a | and therefore a checkable payee name, even if it's empty.)

Transaction comments

Text following ;, after a transaction description, and/or on indented lines immediately below it, form comments for that transaction. They are reproduced by print but otherwise ignored, except they may contain tags, which are not ignored.

2012-01-01 something  ; a transaction comment
    ; a second line of transaction comment
    expenses   1
    assets

Postings

A posting is an addition of some amount to, or removal of some amount from, an account. Each posting line begins with at least one space or tab (2 or 4 spaces is common), followed by:

  • (optional) a status character (empty, !, or *), followed by a space
  • (required) an account name (any text, optionally 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:

assets
assets:bank
assets:bank:checking
expenses
expenses:food

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

assets
 bank
  checking
expenses
 food

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.

Amounts

After the account name, there is usually an amount. (Remember: between account name and amount, there must be two or more spaces.)

hledger's amount format is flexible, supporting several international formats. Here are some examples. Amounts have a number (the "quantity"):

1

..and usually a currency symbol or commodity name (more on this below), to the left or right of the quantity, with or without a separating space:

$1
4000 AAPL
3 "green apples"

Amounts can be preceded by a minus sign (or a plus sign, though plus is the default), The sign can be written before or after a left-side commodity symbol:

-$1
$-1

One or more spaces between the sign and the number are acceptable when parsing (but they won't be displayed in output):

+ $1
$-      1

Scientific E notation is allowed:

1E-6
EUR 1E3

Decimal marks

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

1.23
1,23

Both of these are common in international number formats, so hledger is not biased towards one or the other. Because hledger also supports digit group marks (eg thousands separators), this means that a number like 1,000 or 1.000 containing just one period or comma is ambiguous. In such cases, hledger by default assumes it is a decimal mark, and will parse both of those as 1.

To help hledger parse such ambiguous numbers more accurately, if you use digit group marks, we recommend declaring the decimal mark explicitly. The best way is to add a decimal-mark directive at the top of each data file, like this:

decimal-mark .

Or you can declare it per commodity with commodity directives, described below.

hledger also accepts numbers like 10. with no digits after the decimal mark (and will sometimes display numbers that way to disambiguate them - see Trailing decimal marks).

Digit group marks

In the integer part of the amount quantity (left of the decimal mark), groups of digits can optionally be separated by a digit group mark - a comma or period (whichever is not used as decimal mark), or a space (several Unicode space variants, like no-break space, are also accepted). So these are all valid amounts in a journal file:

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

Commodity

Amounts in hledger have both a "quantity", which is a signed decimal number, and a "commodity", which is a currency symbol, stock ticker, or any word or phrase describing something you are tracking.

If the commodity name contains non-letters (spaces, numbers, or punctuation), you must always write it inside double quotes ("green apples", "ABC123").

If you write just a bare number, that too will have a commodity, with name ""; we call that the "no-symbol commodity".

Actually, hledger combines these single-commodity amounts into more powerful multi-commodity amounts, which are what it works with most of the time. A multi-commodity amount could be, eg: 1 USD, 2 EUR, 3.456 TSLA. In practice, you will only see multi-commodity amounts in hledger's output; you can't write them directly in the journal file.

By default, the format of amounts in the journal influences how hledger displays them in output. This is explained in Commodity display style below.

Costs

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:

    2009/1/1
      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:

    2009/1/1
      assets:euros     €100 @@ $135  ; one hundred euros purchased at $135 for the lot
      assets:dollars
    
  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:

    2009/1/1
      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:

2013/1/1
  a   $1 =  $1
  b      = $-1

2013/1/2
  a   $1 =  $2
  b  $-1 = $-2

After reading a journal file, hledger will check all balance assertions and report an error if any of them fail. Balance assertions can protect you from, eg, inadvertently disrupting reconciled balances while cleaning up old entries. You can disable them temporarily with the -I/--ignore-assertions flag, which can be useful for troubleshooting or for reading Ledger files. (Note: this flag currently does not disable balance assignments, described below).

Assertions and ordering

hledger calculates and checks an account's balance assertions in date order (and when there are multiple assertions on the same day, in parse order). Note this is different from Ledger, which checks assertions always in parse order, ignoring dates.

This means in hledger you can freely reorder transactions, postings, or files, and balance assertions will usually keep working. The exception is when you reorder multiple postings on the same day, to the same account, which have balance assertions; those will likely need updating.

Assertions and multiple 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:

2019/1/1
  (a)     $1 @ €1 = $1

We do allow costs to be written in balance assertion amounts, however, and print shows them, but they don't affect whether the assertion passes or fails. This is for backward compatibility (hledger's close command used to generate balance assertions with costs), and because balance assignments do use costs (see below).

Assertions and commodities

The balance assertions described so far are "single commodity balance assertions": they assert and check the balance in one commodity, ignoring any others that may be present. This is how balance assertions work in Ledger also.

If an account contains multiple commodities, you can assert their balances by writing multiple postings with balance assertions, one for each commodity:

2013/1/1
  usd   $-1
  eur   €-1
  both

2013/1/2
  both    0 = $1
  both    0 = €1

In hledger you can make a stronger "sole commodity balance assertion" by writing two equals signs (== EXPECTEDBALANCE). This also asserts that there are no other commodities in the account besides the asserted one (or at least, that their current balance is zero):

2013/1/1
  usd   $-1  == $-1  ; these sole commodity assertions succeed
  eur   €-1  == €-1
  both      ;==  $1  ; this one would fail because 'both' contains $ and €

It's less easy to make a "sole commodities balance assertion" (note the plural) - ie, asserting that an account contains two or more specified commodities and no others. It can be done by

  1. isolating each commodity in a subaccount, and asserting those
  2. and also asserting there are no commodities in the parent account itself:
2013/1/1
  usd       $-1
  eur       €-1
  both        0 == 0   ; nothing up my sleeve
  both:usd   $1 == $1  ; a dollar here
  both:eur   €1 == €1  ; a euro there

Assertions and subaccounts

All of the balance assertions above (both = and ==) are "subaccount-exclusive balance assertions"; they ignore any balances that exist in deeper subaccounts.

In hledger you can make "subaccount-inclusive balance assertions" by adding a star after the equals (=* or ==*):

2019/1/1
  equity:start
  assets:checking  $10
  assets:savings   $10
  assets            $0 ==* $20  ; assets + subaccounts contains $20 and nothing else

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

2012-01-01
    expenses   1  ; a comment for posting 1
    assets
    ; a comment for posting 2
    ; a second comment line for posting 2

Transaction balancing

How exactly does hledger decide when a transaction is balanced ? 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

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

Directives

Besides transactions, there is something else you can put in a journal file: directives. These are declarations, beginning with a keyword, that modify hledger's behaviour. Some directives can have more specific subdirectives, indented below them. hledger's directives are similar to Ledger's in many cases, but there are also many differences. Directives are not required, but can be useful. Here are the main directives:

purposedirective
READING DATA:
Rewrite account namesalias
Comment out sections of the filecomment
Declare file's decimal mark, to help parse amounts accuratelydecimal-mark
Include other data filesinclude
GENERATING DATA:
Generate recurring transactions or budget goals~
Generate extra postings on existing transactions=
CHECKING FOR ERRORS:
Define valid entities to provide more error checkingaccount, commodity, payee, tag
REPORTING:
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.
N
aliasRewrites account names, in following entries until end of current file or end aliases.
Command line equivalent: --alias
Y
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
N,
N,
Y,
Y
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
N
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.
Y,
Y,
N,
N
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
assets
liabilities
equity
revenues
expenses

If there are undeclared accounts, those will be displayed last, in alphabetical order.

Sorting is done within each group of sibling accounts, at each level of the account tree. Eg, a declaration like account parent:child influences child's position among its siblings.

Note, it does not affect parent's position; for that, you need an account parent declaration.

Sibling accounts are always displayed together; hledger won't display x:y in between a:b and a:c.

An account directive both declares an account as a valid posting target, and declares its display order; you can't easily do one without the other.

Account types

hledger knows that 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.)

Eg:

alias /REGEX/ = REPLACEMENT

or:

$ hledger --alias '/REGEX/=REPLACEMENT' ...

Any part of an account name matched by REGEX will be replaced by REPLACEMENT. REGEX is case-insensitive as usual.

If you need to match a forward slash, escape it with a backslash, eg /\/=:.

If REGEX contains parenthesised match groups, these can be referenced by the usual backslash and number in REPLACEMENT:

alias /^(.+):bank:([^:]+):(.*)/ = \1:\2 \3
; rewrites "assets:bank:wells fargo:checking" to  "assets:wells fargo checking"

REPLACEMENT continues to the end of line (or on command line, to end of option argument), so it can contain trailing whitespace.

Combining aliases

You can define as many aliases as you like, using journal directives and/or command line options.

Recursive aliases - where an account name is rewritten by one alias, then by another alias, and so on - are allowed. Each alias sees the effect of previously applied aliases.

In such cases it can be important to understand which aliases will be applied and in which order. For (each account name in) each journal entry, we apply:

  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
  bar

This means that account aliases should usually be declared at the start of your top-most file, like this:

alias foo=Foo
alias bar=Bar

2023-01-01  ; affected by aliases above
  foo  1
  bar

include c.journal  ; also affected

end aliases directive

You can clear (forget) all currently defined aliases (seen in the journal so far, or defined on the command line) with this directive:

end aliases

Aliases can generate bad account names

Be aware that account aliases can produce malformed account names, which could cause confusing reports or invalid print output. For example, you could erase all account names:

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

The above print output is not a valid journal. Or you could insert an illegal double space, causing print output that would give a different journal when reparsed:

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

Aliases and account types

If an account with a type declaration (see Declaring accounts > Account types) is renamed by an alias, normally the account type remains in effect.

However, renaming in a way that reshapes the account tree (eg renaming parent accounts but not their children, or vice versa) could prevent child accounts from inheriting the account type of their parents.

Secondly, if an account's type is being inferred from its name, renaming it by an alias could prevent or alter that.

If you are using account aliases and the type: query is not matching accounts as you expect, try troubleshooting with the accounts command, eg something like:

$ hledger accounts --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 .

or

decimal-mark ,

This prevents any ambiguity when parsing numbers in the file, so we recommend it, especially if the file contains digit group marks (eg thousands separators).

include directive

You can pull in the content of additional files by writing an include directive, like this:

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

P DATE COMMODITY1SYMBOL COMMODITY2AMOUNT

DATE is a simple date, COMMODITY1SYMBOL is the symbol of the commodity being priced, and COMMODITY2AMOUNT is the amount (symbol and quantity) of commodity 2 that one unit of commodity 1 is worth on this date. Examples:

# one euro was worth $1.35 from 2009-01-01 onward:
P 2009-01-01 € $1.35

# and $1.40 from 2010-01-01 onward:
P 2010-01-01 € $1.40

The -V, -X and --value flags use these market prices to show amount values in another commodity. See Value reporting.

payee directive

payee PAYEE NAME

This directive can be used to declare a limited set of payees which may appear in transaction descriptions. The "payees" check will report an error if any transaction refers to a payee that has not been declared. Eg:

payee Whole Foods    ; a comment

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

To declare the empty payee name, use "".

payee ""

Ledger-style indented subdirectives, if any, are currently ignored.

tag directive

tag TAGNAME

This directive can be used to declare a limited set of tag names allowed in tags. TAGNAME should be a valid tag name (no spaces). Eg:

tag  item-id

Any indented subdirectives are currently ignored.

The "tags" check will report an error if any undeclared tag name is used. It is quite easy to accidentally create a tag through normal use of colons in comments; if you want to prevent this, you can declare and check your tags .

Periodic transactions

The ~ directive declares a "periodic rule" which generates temporary extra transactions, usually recurring at some interval, when hledger is run with the --forecast flag. These "forecast transactions" are useful for forecasting future activity. They exist only for the duration of the report, and only when --forecast is used; they are not saved in the journal file by hledger.

Periodic rules also have a second use: with the --budget flag they set budget goals for budgeting.

Periodic rules can be a little tricky, so before you use them, read this whole section, or at least the following tips:

  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
    assets:bank:checking

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

The period expression is the same syntax used for specifying multi-period reports, just interpreted differently; there, it specifies report periods; here it specifies recurrence dates (the periods' start dates).

Periodic rules and relative dates

Partial or relative dates (like 12/31, 25, tomorrow, last week, next quarter) are usually not recommended in periodic rules, since the results will change as time passes. If used, they will be interpreted relative to, in order of preference:

  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

So,

  • Do write two spaces between your period expression and your transaction description, if any.
  • Don't accidentally write two spaces in the middle of your period expression.

Auto postings

The = directive declares an "auto posting rule", which adds extra postings to existing transactions. (Remember, postings are the account name & amount lines below a transaction's date & description.)

In the journal, an auto posting rule looks quite like a transaction, but instead of date and description it has = (mnemonic: "match") and a query, like this:

= QUERY
    ACCOUNT    AMOUNT
    ...

Queries are just like command line queries; an account name substring is most common. Query terms containing spaces should be enclosed in single or double quotes.

Each = rule works like this: when hledger is run with the --auto flag, wherever the QUERY matches a posting in the journal, the rule's postings are added to that transaction, immediately below the matched posting. Note these generated postings are temporary, existing only for the duration of the report, and only when --auto is used; they are not saved in the journal file by hledger.

Generated postings' amounts can depend on the matched posting's amount. So auto postings can be useful for, eg, adding tax postings with a standard percentage. AMOUNT can be:

  • a number with no commodity symbol, like 2. The matched posting's commodity symbol will be added to this.

  • a normal amount with a commodity symbol, like $2. This will be used as-is.

  • an asterisk followed by a number, like *2. This will multiply the matched posting's amount (and total price, if any) by the number.

  • an asterisk followed by an amount with commodity symbol, like *$2. This multiplies and also replaces the commodity symbol with this new one.

Some examples:

; every time I buy food, schedule a dollar donation
= expenses:food
    (liabilities:charity)   $-1

; when I buy a gift, also deduct that amount from a budget envelope subaccount
= expenses:gifts
    assets:checking:gifts  *-1
    assets:checking         *1

2017/12/1
  expenses:food    $10
  assets:checking

2017/12/14
  expenses:gifts   $20
  assets:checking
$ hledger print --auto
2017-12-01
    expenses:food              $10
    assets:checking
    (liabilities:charity)      $-1

2017-12-14
    expenses:gifts             $20
    assets:checking
    assets:checking:gifts     -$20
    assets:checking            $20

Note that depending fully on generated data such as this has some drawbacks - it's less portable, less future-proof, less auditable by others, and less robust (eg your balance assertions will depend on whether you use or don't use --auto). An alternative is to use auto postings in "one time" fashion - use them to help build a complex journal entry, view it with hledger print --auto, and then copy that output into the journal file to make it permanent.

Auto postings and multiple files

An auto posting rule can affect any transaction in the current file, or in any parent file or child file. Note, currently it will not affect sibling files (when multiple -f/--file are used - see #1212).

Auto postings and dates

A posting date (or secondary date) in the matched posting, or (taking precedence) a posting date in the auto posting rule itself, will also be used in the generated posting.

Auto postings and transaction balancing / inferred amounts / balance assertions

Currently, auto postings are added:

Note this means that journal entries must be balanced both before and after auto postings are added. This changed in hledger 1.12+; see #893 for background.

This also means that you cannot have more than one auto-posting with a missing amount applied to a given transaction, as it will be unable to infer amounts.

Auto posting tags

Automated postings will have some extra tags:

  • generated-posting:= QUERY - shows this was generated by an auto posting rule, and the query
  • _generated-posting:= QUERY - a hidden tag, which does not appear in hledger's output. This can be used to match postings generated "just now", rather than generated in the past and saved to the journal.

Also, any transaction that has been changed by auto posting rules will have these tags added:

  • modified: - this transaction was modified
  • _modified: - a hidden tag not appearing in the comment; this transaction was modified "just now".
Auto postings on forecast transactions only

Tip: you can can make auto postings that will apply to forecast transactions but not recorded transactions, by adding tag:_generated-transaction to their QUERY. This can be useful when generating new journal entries to be saved in the journal.

Other syntax

hledger journal format supports quite a few other features, mainly to make interoperating with or converting from Ledger easier. Note some of the features below are powerful and can be useful in special cases, but in general, features in this section are considered less important or even not recommended for most users. Downsides are mentioned to help you decide if you want to use them.

Balance assignments

Ledger-style balance assignments are also supported. These are like balance assertions, but with no posting amount on the left side of the equals sign; instead it is calculated automatically so as to satisfy the assertion. This can be a convenience during data entry, eg when setting opening balances:

; starting a new journal, set asset account balances
2016/1/1 opening balances
  assets:checking            = $409.32
  assets:savings             = $735.24
  assets:cash                 = $42
  equity:opening balances

or when adjusting a balance to reality:

; no cash left; update balance, record any untracked spending as a generic expense
2016/1/15
  assets:cash    = $0
  expenses:misc

The calculated amount depends on the account's balance in the commodity at that point (which depends on the previously-dated postings of the commodity to that account since the last balance assertion or assignment).

Downsides: using balance assignments makes your journal less explicit; to know the exact amount posted, you have to run hledger or do the calculations yourself, instead of just reading it. Also balance assignments' forcing of balances can hide errors. These things make your financial data less portable, less future-proof, and less trustworthy in an audit.

Balance assignments and costs

A cost in a balance assignment will cause the calculated amount to have that cost attached:

2019/1/1
  (a)             = $1 @ €2
$ hledger print --explicit
2019-01-01
    (a)         $1 @ €2 = $1 @ €2
Balance assignments and multiple files

Balance assignments handle multiple files like balance assertions. They see balance from other files previously included from the current file, but not from previous sibling or parent files.

Bracketed posting dates

For setting posting dates and secondary posting dates, Ledger's bracketed date syntax is also supported: [DATE], [DATE=DATE2] or [=DATE2] in posting comments. hledger will attempt to parse any square-bracketed sequence of the 0123456789/-.= characters in this way. With this syntax, DATE infers its year from the transaction and DATE2 infers its year from DATE.

Downsides: another syntax to learn, redundant with hledger's date:/date2: tags, and confusingly similar to Ledger's lot date syntax.

D directive

D AMOUNT

This directive sets a default commodity, to be used for any subsequent commodityless amounts (ie, plain numbers) seen while parsing the journal. This effect lasts until the next D directive, or the end of the current file.

For compatibility/historical reasons, D also acts like a commodity directive (setting the commodity's decimal mark for parsing and display style for output). So its argument is not just a commodity symbol, but a full amount demonstrating the style. The amount must include a decimal mark (either period or comma). Eg:

; commodity-less amounts should be treated as dollars
; (and displayed with the dollar sign on the left, thousands separators and two decimal places)
D $1,000.00

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

Interactions with other directives:

For setting a commodity's display style, a commodity directive has highest priority, then a D directive.

For detecting a commodity's decimal mark during parsing, decimal-mark has highest priority, then commodity, then D.

For checking commodity symbols with the check command, a commodity directive is required (hledger check commodities ignores D directives).

Downsides: omitting commodity symbols makes your financial data less explicit, less portable, and less trustworthy in an audit. It is usually an unsustainable shortcut; sooner or later you will want to track multiple commodities. D is overloaded with functions redundant with commodity and decimal-mark. And it works differently from Ledger's D.

apply account directive

This directive sets a default parent account, which will be prepended to all accounts in following entries, until an end apply account directive or end of current file. Eg:

apply account home

2010/1/1
    food    $10
    cash

end apply account

is equivalent to:

2010/01/01
    home:food           $10
    home:cash          $-10

account directives are also affected, and so is any included content.

Account names entered via hledger add or hledger-web are not affected.

Account aliases, if any, are applied after the parent account is prepended.

Downsides: this can make your financial data less explicit, less portable, and less trustworthy in an audit.

Y directive

Y YEAR

or (deprecated backward-compatible forms):

year YEAR apply year YEAR

The space is optional. This sets a default year to be used for subsequent dates which don't specify a year. Eg:

Y2009  ; set default year to 2009

12/15  ; equivalent to 2009/12/15
  expenses  1
  assets

year 2010  ; change default year to 2010

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

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

Downsides: omitting the year (from primary transaction dates, at least) makes your financial data less explicit, less portable, and less trustworthy in an audit. Such dates can get separated from their corresponding Y directive, eg when evaluating a region of the journal in your editor. A missing Y directive makes reports dependent on today's date.

Secondary dates

A secondary date is written after the primary date, following an equals sign. 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
python
  PYTHONCODE
tag         NAME
value       EXPR
--command-line-flags

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:

  • @ UNITCOST and @@ TOTALCOST
    • 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:

  • @ UNITCOST and @@ TOTALCOST
    • 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)
  • {UNITCOST} and {{TOTALCOST}}
    • 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.

CSV

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.

source

If you tell hledger to read a csv file with -f foo.csv, it will look for rules in foo.csv.rules. Or, you can tell it to read the rules file, with -f foo.csv.rules, and it will look for data in foo.csv (since 1.30).

These are mostly equivalent, but the second method provides some extra features. For one, the data file can be missing, without causing an error; it is just considered empty. 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".

separator

You can use the separator rule to read other kinds of character-separated data. The argument is any single separator character, or the words tab or space (case insensitive). Eg, for comma-separated values (CSV):

separator ,

or for semicolon-separated values (SSV):

separator ;

or for tab-separated values (TSV):

separator TAB

If the input file has a .csv, .ssv or .tsv file extension (or a csv:, ssv:, tsv: prefix), the appropriate separator will be inferred automatically, and you won't need this rule.

skip

skip N

The word skip followed by a number (or no number, meaning 1) tells hledger to ignore this many non-empty lines at the start of the input data. You'll need this whenever your CSV data contains header lines. Note, empty and blank lines are skipped automatically, so you don't need to count those.

skip has a second meaning: it can be used inside if blocks (described below), to skip one or more records whenever the condition is true. Records skipped in this way are ignored, except they are still required to be valid CSV.

date-format

date-format DATEFMT

This is a helper for the date (and date2) fields. If your CSV dates are not formatted like YYYY-MM-DD, YYYY/MM/DD or YYYY.MM.DD, you'll need to add a date-format rule describing them with a strptime-style date parsing pattern - see https://hackage.haskell.org/package/time/docs/Data-Time-Format.html#v:formatTime. The pattern must parse the CSV date value completely. Some examples:

# MM/DD/YY
date-format %m/%d/%y
# D/M/YYYY
# The - makes leading zeros optional.
date-format %-d/%-m/%Y
# YYYY-Mmm-DD
date-format %Y-%h-%d
# M/D/YYYY HH:MM AM some other junk
# Note the time and junk must be fully parsed, though only the date is used.
date-format %-m/%-d/%Y %l:%M %p some other junk

timezone

timezone TIMEZONE

When CSV contains date-times that are implicitly in some time zone other than yours, but containing no explicit time zone information, you can use this rule to declare the CSV's native time zone, which helps prevent off-by-one dates.

When the CSV date-times do contain time zone information, you don't need this rule; instead, use %Z in date-format (or %z, %EZ, %Ez; see the formatTime link above).

In either of these cases, hledger will do a time-zone-aware conversion, localising the CSV date-times to your current system time zone. If you prefer to localise to some other time zone, eg for reproducibility, you can (on unix at least) set the output timezone with the TZ environment variable, eg:

$ TZ=-1000 hledger print -f foo.csv  # or TZ=-1000 hledger import foo.csv

timezone currently does not understand timezone names, except "UTC", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", or "PDT". For others, use numeric format: +HHMM or -HHMM.

newest-first

hledger tries to ensure that the generated transactions will be ordered chronologically, including same-day transactions. Usually it can auto-detect how the CSV records are ordered. But if it encounters CSV where all records are on the same date, it assumes that the records are oldest first. If in fact the CSV's records are normally newest first, like:

2022-10-01, txn 3...
2022-10-01, txn 2...
2022-10-01, txn 1...

you can add the newest-first rule to help hledger generate the transactions in correct order.

# same-day CSV records are newest first
newest-first

intra-day-reversed

If CSV records within a single day are ordered opposite to the overall record order, you can add the intra-day-reversed rule to improve the order of journal entries. Eg, here the overall record order is newest first, but same-day records are oldest first:

2022-10-02, txn 3...
2022-10-02, txn 4...
2022-10-01, txn 1...
2022-10-01, txn 2...
# transactions within each day are reversed with respect to the overall date order
intra-day-reversed

decimal-mark

decimal-mark .

or:

decimal-mark ,

hledger automatically accepts either period or comma as a decimal mark when parsing numbers (cf Amounts). However if any numbers in the CSV contain digit group marks, such as thousand-separating commas, you should declare the decimal mark explicitly with this rule, to avoid misparsed numbers.

fields list

fields FIELDNAME1, FIELDNAME2, ...

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

  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

HLEDGERFIELD FIELDVALUE

Field assignments are the more flexible way to assign CSV values to hledger fields. They can be used instead of or in addition to a fields list (see above).

To assign a value to a hledger field, write the field name (any of the standard hledger field/pseudo-field names, defined below), a space, followed by a text value on the same line. This text value may interpolate CSV fields, referenced either by their 1-based position in the CSV record (%N) or by the name they were given in the fields list (%CSVFIELD), and regular expression match groups (\N).

Some examples:

# set the amount to the 4th CSV field, with " USD" appended
amount %4 USD

# combine three fields to make a comment, containing note: and date: tags
comment note: %somefield - %anotherfield, date: %1

Tips:

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

or

if
MATCHER
MATCHER
MATCHER
 RULE
 RULE

If any of the matchers succeeds, all of the indented rules will be applied. They are usually field assignments, but the following special rules may also be used within an if block:

  • skip - skips the matched CSV record (generating no transaction from it)
  • end - skips the rest of the current CSV file.

Some examples:

# if the record contains "groceries", set account2 to "expenses:groceries"
if groceries
 account2 expenses:groceries
# if the record contains any of these phrases, set account2 and a transaction comment as shown
if
monthly service fee
atm transaction fee
banking thru software
 account2 expenses:business:banking
 comment  XXX deductible ? check it
# if an empty record is seen (assuming five fields), ignore the rest of the CSV file
if ,,,,
 end

Matchers

There are two kinds:

  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:

if,HLEDGERFIELD1,HLEDGERFIELD2,...
MATCHERA,VALUE1,VALUE2,...
MATCHERB,VALUE1,VALUE2,...
; Comment line that explains MATCHERC
MATCHERC,VALUE1,VALUE2,...
<empty line>

The first character after if is taken to be this if table's field separator. It is unrelated to the separator used in the CSV file. It should be a non-alphanumeric character like , or | that does not appear anywhere else in the table (it should not be used in field names or matchers or values, and it cannot be escaped with a backslash).

Each line must contain the same number of separators; empty values are allowed. Whitespace can be used in the matcher lines for readability (but not in the if line, currently). You can use the comment lines in the table body. The table must be terminated by an empty line (or end of file).

An if table like the above is interpreted as follows: try all of the 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:

if MATCHERA
  HLEDGERFIELD1 VALUE1
  HLEDGERFIELD2 VALUE2
  ...

if MATCHERB
  HLEDGERFIELD1 VALUE1
  HLEDGERFIELD2 VALUE2
  ...

; Comment line which explains MATCHERC
if MATCHERC
  HLEDGERFIELD1 VALUE1
  HLEDGERFIELD2 VALUE2
  ...

Example:

if,account2,comment
atm transaction fee,expenses:business:banking,deductible? check it
%description groceries,expenses:groceries,
;; Comment line that desribes why this particular date is special
2023/01/12.*Plumbing LLC,expenses:house:upkeep,emergency plumbing call-out

balance-type

Balance assertions generated by assigning to balanceN are of the simple = type by default, which is a single-commodity, subaccount-excluding assertion. You may find the subaccount-including variants more useful, eg if you have created some virtual subaccounts of checking to help with budgeting. You can select a different type of assertion with the balance-type rule:

# balance assertions will consider all commodities and all subaccounts
balance-type ==*

Here are the balance assertion types for quick reference:

=    single commodity, exclude subaccounts
=*   single commodity, include subaccounts
==   multi commodity,  exclude subaccounts
==*  multi commodity,  include subaccounts

include

include RULESFILE

This includes the contents of another CSV rules file at this point. RULESFILE is an absolute file path or a path relative to the current file's directory. This can be useful for sharing common rules between several rules files, eg:

# someaccount.csv.rules

## someaccount-specific rules
fields   date,description,amount
account1 assets:someaccount
account2 expenses:misc

## common rules
include categorisation.rules

Working with CSV

Some tips:

Rapid feedback

It's a good idea to get rapid feedback while creating/troubleshooting CSV rules. Here's a good way, using entr from eradman.com/entrproject:

$ ls foo.csv* | entr bash -c 'echo ----; hledger -f foo.csv print desc:SOMEDESC'

A desc: query (eg) is used to select just one, or a few, transactions of interest. "bash -c" is used to run multiple commands, so we can echo a separator each time the command re-runs, making it easier to read the output.

Valid CSV

Note that hledger will only accept valid CSV conforming to RFC 4180, and equivalent SSV and TSV formats (like RFC 4180 but with semicolon or tab as separators). This means, eg:

  • Values may be enclosed in double quotes, or not. Enclosing in single quotes is not allowed. (Eg 'A','B' is rejected.)
  • When values are enclosed in double quotes, spaces outside the quotes are not allowed. (Eg "A", "B" is rejected.)
  • When values are not enclosed in quotes, they may not contain double quotes. (Eg A"A, 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):

2023-01-01,foo,$123.00

you don't have to do anything special for the commodity symbol, it will be assigned as part of the amount. Eg:

fields date,description,amount
2023-01-01 foo
    expenses:unknown         $123.00
    income:unknown          $-123.00

If the currency is provided as a separate CSV field:

2023-01-01,foo,USD,123.00

You can assign that to the currency pseudo-field, which has the special effect of prepending itself to every amount in the transaction (on the left, with no separating space):

fields date,description,currency,amount
2023-01-01 foo
    expenses:unknown       USD123.00
    income:unknown        USD-123.00

Or, you can use a field assignment to construct the amount yourself, with more control. Eg to put the symbol on the right, and separated by a space:

fields date,description,cur,amt
amount %amt %cur
2023-01-01 foo
    expenses:unknown        123.00 USD
    income:unknown         -123.00 USD

Note we used a temporary field name (cur) that is not currency - that would trigger the prepending effect, which we don't want here.

Amount decimal places

When you are reading CSV data, eg with a command like hledger -f foo.csv print, hledger will infer each commodity's decimal precision (and other commodity display styles) from the amounts - much as when reading a journal file without commodity directives (see the link).

Note, the commodity styles are not inferred from the numbers in the original CSV data; rather, they are inferred from the amounts generated by the CSV rules.

When you are importing CSV data with the import command, eg hledger import foo.csv, there's another step: import tries to make the new entries conform to the journal's existing styles. So for each commodity - let's say it's EUR - import will choose:

  1. the style declared for EUR by a commodity directive in the journal
  2. otherwise, the style inferred from EUR amounts in the journal
  3. otherwise, the style inferred from EUR amounts generated by the CSV rules.

TLDR: if import is not generating the precisions or styles you want, add a commodity directive to specify them.

Referencing other fields

In field assignments, you can interpolate only CSV fields, not hledger fields. In the example below, there's both a CSV field and a hledger field named amount1, but %amount1 always means the CSV field, not the hledger field:

# Name the third CSV field "amount1"
fields date,description,amount1

# Set hledger's amount1 to the CSV amount1 field followed by USD
amount1 %amount1 USD

# Set comment to the CSV amount1 (not the amount1 assigned above)
comment %amount1

Here, since there's no CSV amount1 field, %amount1 will produce a literal "amount1":

fields date,description,csvamount
amount1 %csvamount USD
# Can't interpolate amount1 here
comment %amount1

When there are multiple field assignments to the same hledger field, only the last one takes effect. Here, comment's value will be be B, or C if "something" is matched, but never A:

comment A
comment B
if something
 comment C

How CSV rules are evaluated

Here's how to think of CSV rules being evaluated (if you 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:

Date,Details,Debit,Credit,Balance
07/12/2012,LODGMENT       529898,,10.0,131.21
07/12/2012,PAYMENT,5,,126
# bankofireland-checking.csv.rules

# skip the header line
skip

# name the csv fields, and assign some of them as journal entry fields
fields  date, description, amount-out, amount-in, balance

# We generate balance assertions by assigning to "balance"
# above, but you may sometimes need to remove these because:
#
# - the CSV balance differs from the true balance,
#   by up to 0.0000000000005 in my experience
#
# - it is sometimes calculated based on non-chronological ordering,
#   eg when multiple transactions clear on the same day

# date is in UK/Ireland format
date-format  %d/%m/%Y

# set the currency
currency  EUR

# set the base account for all txns
account1  assets:bank:boi:checking
$ hledger -f bankofireland-checking.csv print
2012-12-07 LODGMENT       529898
    assets:bank:boi:checking         EUR10.0 = EUR131.2
    income:unknown                  EUR-10.0

2012-12-07 PAYMENT
    assets:bank:boi:checking         EUR-5.0 = EUR126.0
    expenses:unknown                  EUR5.0

The balance assertions don't raise an error above, because we're reading directly from CSV, but they will be checked if these entries are imported into a journal file.

Coinbase

A simple example with some CSV from Coinbase. The spot price is recorded using cost notation. The legacy amount field name conveniently sets amount 2 (posting 2's amount) to the total cost.

# Timestamp,Transaction Type,Asset,Quantity Transacted,Spot Price Currency,Spot Price at Transaction,Subtotal,Total (inclusive of fees and/or spread),Fees and/or Spread,Notes
# 2021-12-30T06:57:59Z,Receive,USDC,100,GBP,0.740000,"","","","Received 100.00 USDC from an external account"
# coinbase.csv.rules
skip         1
fields       Timestamp,Transaction_Type,Asset,Quantity_Transacted,Spot_Price_Currency,Spot_Price_at_Transaction,Subtotal,Total,Fees_Spread,Notes
date         %Timestamp
date-format  %Y-%m-%dT%T%Z
description  %Notes
account1     assets:coinbase:cc
amount       %Quantity_Transacted %Asset @ %Spot_Price_at_Transaction %Spot_Price_Currency
$ hledger print -f coinbase.csv
2021-12-30 Received 100.00 USDC from an external account
    assets:coinbase:cc    100 USDC @ 0.740000 GBP
    income:unknown                 -74.000000 GBP

Amazon

Here we convert amazon.com order history, and use an if block to generate a third posting if there's a fee. (In practice you'd probably get this data from your bank instead, but it's an example.)

"Date","Type","To/From","Name","Status","Amount","Fees","Transaction ID"
"Jul 29, 2012","Payment","To","Foo.","Completed","$20.00","$0.00","16000000000000DGLNJPI1P9B8DKPVHL"
"Jul 30, 2012","Payment","To","Adapteva, Inc.","Completed","$25.00","$1.00","17LA58JSKRD4HDGLNJPI1P9B8DKPVHL"
# amazon-orders.csv.rules

# skip one header line
skip 1

# name the csv fields, and assign the transaction's date, amount and code.
# Avoided the "status" and "amount" hledger field names to prevent confusion.
fields date, _, toorfrom, name, amzstatus, amzamount, fees, code

# how to parse the date
date-format %b %-d, %Y

# combine two fields to make the description
description %toorfrom %name

# save the status as a tag
comment     status:%amzstatus

# set the base account for all transactions
account1    assets:amazon
# leave amount1 blank so it can balance the other(s).
# I'm assuming amzamount excludes the fees, don't remember

# set a generic account2
account2    expenses:misc
amount2     %amzamount
# and maybe refine it further:
#include categorisation.rules

# add a third posting for fees, but only if they are non-zero.
if %fees [1-9]
 account3    expenses:fees
 amount3     %fees
$ hledger -f amazon-orders.csv print
2012-07-29 (16000000000000DGLNJPI1P9B8DKPVHL) To Foo.  ; status:Completed
    assets:amazon
    expenses:misc          $20.00

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

Paypal

Here's a real-world rules file for (customised) Paypal CSV, with some Paypal-specific rules, and a second rules file included:

"Date","Time","TimeZone","Name","Type","Status","Currency","Gross","Fee","Net","From Email Address","To Email Address","Transaction ID","Item Title","Item ID","Reference Txn ID","Receipt ID","Balance","Note"
"10/01/2019","03:46:20","PDT","Calm Radio","Subscription Payment","Completed","USD","-6.99","0.00","-6.99","[email protected]","[email protected]","60P57143A8206782E","MONTHLY - $1 for the first 2 Months: Me - Order 99309. Item total: $1.00 USD first 2 months, then $6.99 / Month","","I-R8YLY094FJYR","","-6.99",""
"10/01/2019","03:46:20","PDT","","Bank Deposit to PP Account ","Pending","USD","6.99","0.00","6.99","","[email protected]","0TU1544T080463733","","","60P57143A8206782E","","0.00",""
"10/01/2019","08:57:01","PDT","Patreon","PreApproved Payment Bill User Payment","Completed","USD","-7.00","0.00","-7.00","[email protected]","[email protected]","2722394R5F586712G","Patreon* Membership","","B-0PG93074E7M86381M","","-7.00",""
"10/01/2019","08:57:01","PDT","","Bank Deposit to PP Account ","Pending","USD","7.00","0.00","7.00","","[email protected]","71854087RG994194F","Patreon* Membership","","2722394R5F586712G","","0.00",""
"10/19/2019","03:02:12","PDT","Wikimedia Foundation, Inc.","Subscription Payment","Completed","USD","-2.00","0.00","-2.00","[email protected]","[email protected]","K9U43044RY432050M","Monthly donation to the Wikimedia Foundation","","I-R5C3YUS3285L","","-2.00",""
"10/19/2019","03:02:12","PDT","","Bank Deposit to PP Account ","Pending","USD","2.00","0.00","2.00","","[email protected]","3XJ107139A851061F","","","K9U43044RY432050M","","0.00",""
"10/22/2019","05:07:06","PDT","Noble Benefactor","Subscription Payment","Completed","USD","10.00","-0.59","9.41","[email protected]","[email protected]","6L8L1662YP1334033","Joyful Systems","","I-KC9VBGY2GWDB","","9.41",""
# paypal-custom.csv.rules

# Tips:
# Export from Activity -> Statements -> Custom -> Activity download
# Suggested transaction type: "Balance affecting"
# Paypal's default fields in 2018 were:
# "Date","Time","TimeZone","Name","Type","Status","Currency","Gross","Fee","Net","From Email Address","To Email Address","Transaction ID","Shipping Address","Address Status","Item Title","Item ID","Shipping and Handling Amount","Insurance Amount","Sales Tax","Option 1 Name","Option 1 Value","Option 2 Name","Option 2 Value","Reference Txn ID","Invoice Number","Custom Number","Quantity","Receipt ID","Balance","Address Line 1","Address Line 2/District/Neighborhood","Town/City","State/Province/Region/County/Territory/Prefecture/Republic","Zip/Postal Code","Country","Contact Phone Number","Subject","Note","Country Code","Balance Impact"
# This rules file assumes the following more detailed fields, configured in "Customize report fields":
# "Date","Time","TimeZone","Name","Type","Status","Currency","Gross","Fee","Net","From Email Address","To Email Address","Transaction ID","Item Title","Item ID","Reference Txn ID","Receipt ID","Balance","Note"

fields date, time, timezone, description_, type, status_, currency, grossamount, feeamount, netamount, fromemail, toemail, code, itemtitle, itemid, referencetxnid, receiptid, balance, note

skip  1

date-format  %-m/%-d/%Y

# ignore some paypal events
if
In Progress
Temporary Hold
Update to
 skip

# add more fields to the description
description %description_ %itemtitle

# save some other fields as tags
comment  itemid:%itemid, fromemail:%fromemail, toemail:%toemail, time:%time, type:%type, status:%status_

# convert to short currency symbols
if %currency USD
 currency $
if %currency EUR
 currency E
if %currency GBP
 currency P

# generate postings

# the first posting will be the money leaving/entering my paypal account
# (negative means leaving my account, in all amount fields)
account1 assets:online:paypal
amount1  %netamount

# the second posting will be money sent to/received from other party
# (account2 is set below)
amount2  -%grossamount

# if there's a fee, add a third posting for the money taken by paypal.
if %feeamount [1-9]
 account3 expenses:banking:paypal
 amount3  -%feeamount
 comment3 business:

# choose an account for the second posting

# override the default account names:
# if the amount is positive, it's income (a debit)
if %grossamount ^[^-]
 account2 income:unknown
# if negative, it's an expense (a credit)
if %grossamount ^-
 account2 expenses:unknown

# apply common rules for setting account2 & other tweaks
include common.rules

# apply some overrides specific to this csv

# Transfers from/to bank. These are usually marked Pending,
# which can be disregarded in this case.
if
Bank Account
Bank Deposit to PP Account
 description %type for %referencetxnid %itemtitle
 account2 assets:bank:wf:pchecking
 account1 assets:online:paypal

# Currency conversions
if Currency Conversion
 account2 equity:currency conversion
# common.rules

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

if
Calm Radio
 account2 expenses:online:apps

if
electronic frontier foundation
Patreon
wikimedia
Advent of Code
 account2 expenses:dues

if Google
 account2 expenses:online:apps
 description google | music
$ hledger -f paypal-custom.csv  print
2019-10-01 (60P57143A8206782E) Calm Radio MONTHLY - $1 for the first 2 Months: Me - Order 99309. Item total: $1.00 USD first 2 months, then $6.99 / Month  ; itemid:, fromemail:[email protected], toemail:[email protected], time:03:46:20, type:Subscription Payment, status:Completed
    assets:online:paypal          $-6.99 = $-6.99
    expenses:online:apps           $6.99

2019-10-01 (0TU1544T080463733) Bank Deposit to PP Account for 60P57143A8206782E  ; itemid:, fromemail:, toemail:[email protected], time:03:46:20, type:Bank Deposit to PP Account, status:Pending
    assets:online:paypal               $6.99 = $0.00
    assets:bank:wf:pchecking          $-6.99

2019-10-01 (2722394R5F586712G) Patreon Patreon* Membership  ; itemid:, fromemail:[email protected], toemail:[email protected], time:08:57:01, type:PreApproved Payment Bill User Payment, status:Completed
    assets:online:paypal          $-7.00 = $-7.00
    expenses:dues                  $7.00

2019-10-01 (71854087RG994194F) Bank Deposit to PP Account for 2722394R5F586712G Patreon* Membership  ; itemid:, fromemail:, toemail:[email protected], time:08:57:01, type:Bank Deposit to PP Account, status:Pending
    assets:online:paypal               $7.00 = $0.00
    assets:bank:wf:pchecking          $-7.00

2019-10-19 (K9U43044RY432050M) Wikimedia Foundation, Inc. Monthly donation to the Wikimedia Foundation  ; itemid:, fromemail:[email protected], toemail:[email protected], time:03:02:12, type:Subscription Payment, status:Completed
    assets:online:paypal             $-2.00 = $-2.00
    expenses:dues                     $2.00
    expenses:banking:paypal      ; business:

2019-10-19 (3XJ107139A851061F) Bank Deposit to PP Account for K9U43044RY432050M  ; itemid:, fromemail:, toemail:[email protected], time:03:02:12, type:Bank Deposit to PP Account, status:Pending
    assets:online:paypal               $2.00 = $0.00
    assets:bank:wf:pchecking          $-2.00

2019-10-22 (6L8L1662YP1334033) Noble Benefactor Joyful Systems  ; itemid:, fromemail:[email protected], toemail:[email protected], time:05:07:06, type:Subscription Payment, status:Completed
    assets:online:paypal                       $9.41 = $9.41
    revenues:foss donations:darcshub         $-10.00  ; business:
    expenses:banking:paypal                    $0.59  ; business:

Timeclock

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

timedot format is hledger's human-friendly time logging format. Compared to timeclock format, it is more convenient for quick, approximate, and retroactive time logging, and more human-readable (you can see at a glance where time was spent). A quick example:

2023-05-01
hom:errands          .... ....  ; two hours; the space is ignored
fos:hledger:timedot  ..         ; half an hour
per:admin:finance               ; no time spent yet

hledger reads this as a transaction on this day with three (unbalanced) postings, where each dot represents "0.25". No commodity symbol is assumed, but we typically interpret it as hours.

$ hledger -f a.timedot print   # .timedot file extension (or timedot: prefix) is required
2023-05-01 *
    (hom:errands)                    2.00  ; two hours
    (fos:hledger:timedot)            0.50  ; half an hour
    (per:admin:finance)                 0

A timedot file contains a series of transactions (usually one per day). Each begins with a simple date (Y-M-D, Y/M/D, or Y.M.D), optionally be followed on the same line by a transaction description, and/or a transaction comment following a semicolon.

After the date line are zero or more time postings, consisting of:

  • An account name - any hledger-style account name, optionally indented.

  • Two or more spaces - required if there is an amount (as in journal format).

  • A timedot amount, which can be

    • empty (representing zero)

    • a number, optionally followed by a unit s, m, h, d, w, mo, 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

Numbers:

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

Dots:

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

2016/2/2
inc:client1   .... ....
biz:research  .
$ hledger -f a.timedot print date:2016/2/2
2016-02-02 *
    (inc:client1)          2.00

2016-02-02 *
    (biz:research)          0.25
$ hledger -f a.timedot bal --daily --tree
Balance changes in 2016-02-01-2016-02-03:

            ||  2016-02-01d  2016-02-02d  2016-02-03d 
============++========================================
 biz        ||         0.25         0.25         1.00 
   research ||         0.25         0.25         1.00 
 fos        ||         1.50            0         3.00 
   haskell  ||         1.50            0            0 
   hledger  ||            0            0         3.00 
 inc        ||         6.00         2.00         4.00 
   client1  ||         6.00         2.00         4.00 
------------++----------------------------------------
            ||         7.75         2.25         8.00 

Letters:

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

2023-11-01
work:adm  ccecces
$ hledger -f a.timedot print
2023-11-01
    (work:adm)  1     ; t:c
    (work:adm)  0.5   ; t:e
    (work:adm)  0.25  ; t:s
$ hledger -f a.timedot bal
                1.75  work:adm
--------------------
                1.75  
$ hledger -f a.timedot bal --pivot t
                1.00  c
                0.50  e
                0.25  s
--------------------
                1.75  

Org:

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

Using . as account name separator:

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

PART 3: REPORTING CONCEPTS

Amount formatting

Commodity display style

For the amounts in each commodity, hledger chooses a consistent display style (symbol placement, decimal mark and digit group marks, number of decimal digits) to use in most reports. This is inferred as follows:

First, if there's a D directive declaring a default commodity, that commodity symbol and amount format is applied to all no-symbol amounts in the journal.

Then each commodity's display style is determined from its commodity directive. We recommend always declaring commodities with commodity directives, since they help ensure consistent display styles and precisions, and bring other benefits such as error checking for commodity symbols. Here's an example:

# Set display styles (and decimal marks, for parsing, if there is no decimal-mark directive)
# for the $, EUR, INR and no-symbol commodities:
commodity $1,000.00
commodity EUR 1.000,00
commodity INR 9,99,99,999.00
commodity 1 000 000.9455

But for convenience, if a commodity directive is not present, hledger infers a commodity's display styles from its amounts as they are written in the journal (excluding cost amounts and amounts in periodic transaction rules or auto posting rules). It uses

  • the symbol placement and decimal mark of the first amount seen
  • the digit group marks of the first amount with digit group marks
  • and the maximum number of decimal digits seen across all amounts.

And as fallback if no applicable amounts are found, it would use a default style, like $1000.00 (symbol on the left with no space, period as decimal mark, and two decimal digits).

Finally, commodity styles can be overridden by the -c/--commodity-style command line option.

Rounding

Amounts are stored internally as decimal numbers with up to 255 decimal places. They are displayed with their original journal precisions by print and print-like reports, and rounded to their display precision (the number of decimal digits specified by the commodity display style) by other reports. When rounding, hledger uses banker's rounding (it rounds to the nearest even digit). So eg 0.5 displayed with zero decimal digits appears as "0".

Trailing decimal marks

If you're wondering why your print report sometimes shows trailing decimal marks, with no decimal digits; it does this when showing amounts that have digit group marks but no decimal digits, to disambiguate them and allow them to be re-parsed reliably (see Decimal marks). Eg:

commodity $1,000.00

2023-01-02
    (a)      $1000
$ hledger print
2023-01-02
    (a)        $1,000.

If this is a problem (eg when exporting to Ledger), you can avoid it by disabling digit group marks, eg with -c/--commodity (for each affected commodity):

$ hledger print -c '$1000.00'
2023-01-02
    (a)          $1000

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

$ hledger print -c '$1,000.00' --round=soft
2023-01-02
    (a)      $1,000.00

Amount parseability

More generally, hledger output falls into three rough categories, which format amounts a little bit differently to suit different consumers:

1. "hledger-readable output" - should be readable by hledger (and by humans)

  • This is produced by reports that show full journal entries: print, import, close, 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).

Examples:

-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 -)
date:..12/1
date:thismonth..
date:thismonth

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"
-p2009/1/1to2009/4/1
-p2009/1/1..2009/4/1

Dates are smart dates, so if the current year is 2009, these are also equivalent to the above:

-p "1/1 4/1"
-p "jan-apr"
-p "this year to 4/1"

If you specify only one date, the missing start or end date will be the earliest or latest transaction date in the journal:

-p "from 2009/1/1"everything after january 1, 2009
-p "since 2009/1"the same, since is a synonym
-p "from 2009"the same
-p "to 2009"everything before january 1, 2009

You can also specify a period by writing a single partial or full date:

-p "2009"the year 2009; equivalent to "2009/1/1 to 2010/1/1"
-p "2009/1"the month of january 2009; equivalent to "2009/1/1 to 2009/2/1"
-p "2009/1/1"the first day of 2009; equivalent to "2009/1/1 to 2009/1/2"

or by using the "Q" quarter-year syntax (case insensitive):

-p "2009Q1"first quarter of 2009, equivalent to "2009/1/1 to 2009/4/1"
-p "q4"fourth quarter of the current year

Period expressions with a report interval

A period expression can also begin with a report interval, separated from the start/end dates (if any) by a space or the word in:

-p "weekly from 2009/1/1 to 2009/4/1"
-p "monthly in 2008"
-p "quarterly"

More complex report intervals

Some more complex intervals can be specified within period expressions, such as:

  • biweekly (every two weeks)
  • 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)

Examples:

-p "bimonthly from 2008"
-p "every 2 weeks"
-p "every 5 months from 2009/03"
-p "every 2nd day of week"periods will go from Tue to Tue
-p "every Tue"same
-p "every 15th day"period boundaries will be on 15th of each month
-p "every 2nd Monday"period boundaries will be on second Monday of each month
-p "every 11/05"yearly periods with boundaries on 5th of November
-p "every 5th November"same
-p "every Nov 5th"same

Show historical balances at end of the 15th day of each month (N is an end date, exclusive as always):

$ hledger balance -H -p "every 16th day"

Group postings from the start of wednesday to end of the following tuesday (N is both (inclusive) start date and (exclusive) end date):

$ hledger register checking -p "every 3rd day of week"

Multiple weekday intervals

This special form is also supported:

  • every WEEKDAYNAME,WEEKDAYNAME,... (full or three-letter english weekday names, case insensitive)

Also, weekday and weekendday are shorthand for mon,tue,wed,thu,fri and sat,sun.

This is mainly intended for use with --forecast, to generate periodic transactions on arbitrary days of the week. It may be less useful with -p, since it divides each week into subperiods of unequal length, which is unusual. (Related: #1632)

Examples:

-p "every mon,wed,fri"dates will be Mon, Wed, Fri;
periods will be Mon-Tue, Wed-Thu, Fri-Sun
-p "every weekday"dates will be Mon, Tue, Wed, Thu, Fri;
periods will be Mon, Tue, Wed, Thu, Fri-Sun
-p "every weekendday"dates will be Sat, Sun;
periods will be Sat, Sun-Fri

Depth

With the --depth NUM option (short form: -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.

Queries

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:

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

    '^expenses\b'
    'food$'
    'fuel|repair'
    'accounts (payable|receivable)'

  • To match something other than account name, add one of the query type prefixes described in "Query types" below:

    date:202312-
    status:
    desc:amazon
    cur:USD
    cur:\\$
    amt:'>0'

  • Add a not: prefix to negate a term:

    not:status:'*'
    not:desc:'opening|closing'
    not:cur:USD

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

acct:REGEX or REGEX
Match account names containing this case insensitive regular expression. This is the default query type, so we usually don't bother writing the "acct:" prefix.

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

code:REGEX
Match by transaction code (eg check number).

cur:REGEX
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:\\$.

desc:REGEX
Match transaction descriptions.

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

date2:PERIODEXPR
Match secondary dates within the specified period (independent of the --date2 flag).

depth:N
Match (or display, depending on command) accounts at or above this depth.

expr:"TERM AND NOT (TERM OR TERM)" (eg)
Match with a boolean combination of queries (which must be enclosed in quotes). See Combining query terms below.

note:REGEX
Match transaction notes (the part of the description right of |, or the whole description if there's no |).

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

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

tag:REGEX[=REGEX]
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.

(inacct:ACCTNAME
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.)

Pivoting

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

Pivoted balance report, using member: tag values instead:

$ hledger balance --pivot member
               2 EUR
              -2 EUR  John Doe
--------------------
                   0

One way to show only amounts with a member: value (using a query):

$ hledger balance --pivot member tag:member=.
              -2 EUR  John Doe
--------------------
              -2 EUR

Another way (the acct: query matches against the pivoted "account name"):

$ hledger balance --pivot member acct:.
              -2 EUR  John Doe
--------------------
              -2 EUR

Hierarchical reports can be generated with multiple 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

Forecasting, or speculative future reporting, can be useful for estimating future balances, or for exploring different future scenarios.

The simplest and most flexible way to do it with hledger is to manually record a bunch of future-dated transactions. You could keep these in a separate future.journal and include that with -f only when you want to see them.

--forecast

There is another way: with the --forecast option, hledger can generate temporary "forecast transactions" for reporting purposes, according to periodic transaction rules defined in the journal. Each rule can generate multiple recurring transactions, so by changing one rule you can change many forecasted transactions.

Forecast transactions usually start after ordinary transactions end. By default, they begin after your latest-dated ordinary transaction, or today, whichever is later, and they end six months from today. (The exact rules are a little more complicated, and are given below.)

This is the "forecast period", which need not be the same as the report period. You can override it - eg to forecast farther into the future, or to force forecast transactions to overlap your ordinary transactions - by giving the --forecast option a period expression argument, like --forecast=..2099 or --forecast=2023-02-15... Note that the = is required.

Inspecting forecast transactions

print is the best command for inspecting and troubleshooting forecast transactions. Eg:

~ monthly from 2022-12-20    rent
    assets:bank:checking
    expenses:rent           $1000
$ hledger print --forecast --today=2023/4/21
2023-05-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    assets:bank:checking
    expenses:rent                  $1000

2023-06-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    assets:bank:checking
    expenses:rent                  $1000

2023-07-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    assets:bank:checking
    expenses:rent                  $1000

2023-08-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    assets:bank:checking
    expenses:rent                  $1000

2023-09-20 rent
    ; generated-transaction: ~ monthly from 2022-12-20
    assets:bank:checking
    expenses:rent                  $1000

Here there are no ordinary transactions, so the forecasted transactions begin on the first 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).

Budgeting

With the balance command's --budget report, each periodic transaction rule generates recurring budget goals in specified accounts, and goals and actual performance can be compared. See the balance command's doc below.

You can generate budget goals and forecast transactions at the same time, from the same or different periodic transaction rules: hledger bal -M --budget --forecast ...

See also: Budgeting and Forecasting.

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

2022-01-01
  assets:dollars    $-135
  assets:euros       €100 @ $1.35   ; $1.35 per euro (unit cost)

Variant 2

2022-01-01
  assets:dollars    $-135
  assets:euros       €100 @@ $135   ; $135 total cost

Typically, writing the unit cost (variant 1) is preferable; it can be more effort, requiring more attention to decimal digits; but it reveals the per-unit cost basis, and makes stock sales easier.

Costs can also be left implicit, and hledger will infer the cost that is consistent with a balanced transaction:

Variant 3

2022-01-01
  assets:dollars    $-135
  assets:euros       €100

Here, hledger will attach a @@ €100 cost to the first amount (you can see it with hledger print -x). This form looks convenient, but there are downsides:

  • It sacrifices some error checking. For example, if you accidentally wrote €10 instead of €100, hledger would not be able to detect the mistake.

  • It is sensitive to the order of postings - if they were reversed, a different entry would be inferred and reports would be different.

  • The per-unit cost basis is not easy to read.

So generally this kind of entry is not recommended. You can make sure you have none of these by using -s (strict mode), or by running hledger check balanced.

Reporting at cost

Now when you add the -B/--cost flag to reports ("B" is from Ledger's -B/--basis/--cost flag), any amounts which have been annotated with costs will be converted to their cost's commodity (in the report output). Ie they will be displayed "at cost" or "at sale price".

Some things to note:

  • Costs are attached to specific posting amounts in specific transactions, and once recorded they do not change. This contrasts with market prices, which are ambient and fluctuating.

  • Conversion to cost is performed before conversion to market value (described below).

Equity conversion postings

There is a problem with the entries above - they are not conventional Double Entry Bookkeeping (DEB) notation, and because of the "magical" transformation of one commodity into another, they cause an imbalance in the Accounting Equation. This shows up as a non-zero grand total in balance reports like hledger bse.

For most hledger users, this doesn't matter in practice and can safely be ignored ! But if you'd like to learn more, keep reading.

Conventional DEB uses an extra pair of equity postings to balance the transaction. Of course you can do this in hledger as well:

Variant 4

2022-01-01
    assets:dollars      $-135
    assets:euros         €100
    equity:conversion    $135
    equity:conversion   €-100

Now the transaction is perfectly balanced according to standard DEB, and hledger bse's total will not be disrupted.

And, hledger can still infer the cost for cost reporting, but it's not done by default - you must add the --infer-costs flag like so:

$ hledger print --infer-costs
2022-01-01 one hundred euros purchased at $1.35 each
    assets:dollars       $-135 @@ €100
    assets:euros                  €100
    equity:conversion             $135
    equity:conversion            €-100
$ hledger bal --infer-costs -B
               €-100  assets:dollars                                                                                                                                              
                €100  assets:euros                                                                                                                                                
--------------------                                                                                                                                                              
                   0                                                                                                                                                              

Here are some downsides of this kind of entry:

  • The per-unit cost basis is not easy to read.

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

2022-01-01
  assets:dollars  -$135
  assets:euros     €100 @ $1.35
$ hledger print --infer-equity
2022-01-01
    assets:dollars                    $-135
    assets:euros               €100 @ $1.35
    equity:conversion:$-€:€           €-100
    equity:conversion:$-€:$         $135.00

The equity account names will be "equity:conversion:A-B:A" and "equity:conversion:A-B:B" where A is the alphabetically first commodity symbol. You can customise the "equity:conversion" part by declaring an account with the V/Conversion account type.

Combining costs and equity conversion postings

Finally, you can use both the @/@@ cost notation and equity postings at the same time. This in theory gives the best of all worlds - preserving the accounting equation, revealing the per-unit cost basis, and providing more flexibility in how you write the entry:

Variant 5

2022-01-01 one hundred euros purchased at $1.35 each
    assets:dollars      $-135
    equity:conversion    $135
    equity:conversion   €-100
    assets:euros         €100 @ $1.35

All the other variants above can (usually) be rewritten to this final form with:

$ hledger print -x --infer-costs --infer-equity

Downsides:

  • The precise format of the journal entry becomes more important. If hledger can't detect and match up the cost and equity postings, it will give a transaction balancing error.

  • The add command does not yet accept this kind of entry (#2056).

  • This is the most verbose form.

Requirements for detecting equity conversion postings

--infer-costs has certain requirements (unlike --infer-equity, which always works). It will infer costs only in transactions with:

  • Two non-equity postings, in different commodities. Their order is significant: the cost will be added to the first of them.

  • Two postings to equity conversion accounts, next to one another, which balance the two non-equity postings. This balancing is checked to the same precision (number of decimal places) used in the conversion posting's amount. Equity conversion accounts are:

    • any accounts declared with account type V/Conversion, or their subaccounts
    • otherwise, accounts named equity:conversion, equity:trade, 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
2016/11/3
    assets:euros        €100
    assets:checking

; the euro is worth fewer dollars by dec 21
P 2016/12/21 € $1.03

How many euros do I have ?

$ hledger -f t.j bal -N euros
                €100  assets:euros

What are they worth at end of nov 3 ?

$ hledger -f t.j bal -N euros -V -e 2016/11/4
             $110.00  assets:euros

What are they worth after 2016/12/21 ? (no report end date specified, defaults to today)

$ hledger -f t.j bal -N euros -V
             $103.00  assets:euros

Here are some examples showing the effect of --value, as seen with print:

P 2000-01-01 A  1 B
P 2000-02-01 A  2 B
P 2000-03-01 A  3 B
P 2000-04-01 A  4 B

2000-01-01
  (a)      1 A @ 5 B

2000-02-01
  (a)      1 A @ 6 B

2000-03-01
  (a)      1 A @ 7 B

Show the cost of each posting:

$ hledger -f- print --cost
2000-01-01
    (a)             5 B

2000-02-01
    (a)             6 B

2000-03-01
    (a)             7 B

Show the value as of the last day of the report period (2000-02-29):

$ hledger -f- print --value=end date:2000/01-2000/03
2000-01-01
    (a)             2 B

2000-02-01
    (a)             2 B

With no report period specified, that shows the value as of the last day of the journal (2000-03-01):

$ hledger -f- print --value=end
2000-01-01
    (a)             3 B

2000-02-01
    (a)             3 B

2000-03-01
    (a)             3 B

Show the current value (the 2000-04-01 price is still in effect today):

$ hledger -f- print --value=now
2000-01-01
    (a)             4 B

2000-02-01
    (a)             4 B

2000-03-01
    (a)             4 B

Show the value on 2000/01/15:

$ hledger -f- print --value=2000-01-15
2000-01-01
    (a)             1 B

2000-02-01
    (a)             1 B

2000-03-01
    (a)             1 B

Interaction of valuation and queries

When matching postings based on queries in the presence of valuation, the following happens:

  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
print
posting amountscostvalue at report end or todayvalue at posting datevalue at report or journal endvalue at DATE/today
balance assertions/assignmentsunchangedunchangedunchangedunchangedunchanged

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

PART 4: COMMANDS

Commands overview

Here are the built-in commands:

DATA ENTRY

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

DATA CREATION

  • close - generate balance-zeroing/restoring transactions
  • rewrite - generate auto postings, like print --auto

DATA MANAGEMENT

  • check - check for various kinds of error in the data
  • diff - compare account transactions in two journal files

REPORTS, FINANCIAL

REPORTS, VERSATILE

  • 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

REPORTS, BASIC

  • 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

  • help - show the hledger manual with info/man/pager
  • demo - show small hledger demos in the terminal

ADD-ONS

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.

accounts

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.

Examples:

$ hledger accounts
assets:bank:checking
assets:bank:saving
assets:cash
expenses:food
expenses:supplies
income:gifts
income:salary
liabilities:debts
$ hledger accounts --undeclared --directives >> $LEDGER_FILE
$ hledger check accounts

activity

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.

Examples:

$ hledger activity --quarterly
2008-01-01 **
2008-04-01 *******
2008-07-01 
2008-10-01 **

add

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.

Features:

  • add tries to provide useful defaults, using the most similar (by description) recent transaction (filtered by the query, if any) as a template.
  • You can also set the initial defaults with command line arguments.
  • Readline-style edit keys can be used during data entry.
  • The tab key will auto-complete whenever possible - accounts, payees/descriptions, dates (yesterday, today, tomorrow). If the input area is empty, it will insert the default value.
  • 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]: 
Saved.
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.

aregister

(areg)

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.

balance

(bal)

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)

..in..

..either..

  • per period (the default)
  • or accumulated since report start date (--cumulative)
  • or accumulated since account creation (--historical/-H)

..possibly converted to..

..with..

  • totals (-T), averages (-A), percentages (-%), inverted sign (--invert)
  • rows and columns swapped (--transpose)
  • another field used as account name (--pivot)
  • custom-formatted line items (single-period reports only) (--format)
  • commodities displayed on the same line or multiple lines (--layout)

This command supports the output destination and output format options, with output formats txt, csv, tsv (Added in 1.32), json, and (multi-period reports only:) html. In txt output in a colour-supporting terminal, negative amounts are shown in red.

Simple balance report

With no arguments, balance shows a list of all accounts and their change of balance - ie, the sum of posting amounts, both inflows and outflows - during the entire period of the journal. ("Simple" here means just one column of numbers, covering a single period. You can also have multi-period reports, described later.)

For real-world accounts, these numbers will normally be their end balance at the end of the journal period; more on this below.

Accounts are sorted by declaration order if any, and then alphabetically by account name. For instance (using examples/sample.journal):

$ hledger -f examples/sample.journal bal
                  $1  assets:bank:saving
                 $-2  assets:cash
                  $1  expenses:food
                  $1  expenses:supplies
                 $-1  income:gifts
                 $-1  income:salary
                  $1  liabilities:debts
--------------------
                   0  

Accounts with a zero balance (and no non-zero subaccounts, in tree mode - see below) are hidden by default. Use -E/--empty to show them (revealing assets:bank:checking here):

$ hledger -f examples/sample.journal bal  -E
                   0  assets:bank:checking
                  $1  assets:bank:saving
                 $-2  assets:cash
                  $1  expenses:food
                  $1  expenses:supplies
                 $-1  income:gifts
                 $-1  income:salary
                  $1  liabilities:debts
--------------------
                   0  

The total of the amounts displayed is shown as the last line, unless -N/--no-total is used.

Balance report line format

For single-period balance reports displayed in the terminal (only), you can use --format FMT to customise the format and content of each line. Eg:

$ hledger -f examples/sample.journal balance --format "%20(account) %12(total)"
              assets          $-1
         bank:saving           $1
                cash          $-2
            expenses           $2
                food           $1
            supplies           $1
              income          $-2
               gifts          $-1
              salary          $-1
   liabilities:debts           $1
---------------------------------
                                0

The FMT format string specifies the formatting applied to each account/balance pair. It may contain any suitable text, with data fields interpolated like so:

%[MIN][.MAX](FIELDNAME)

  • MIN pads with spaces to at least this width (optional)

  • MAX truncates at this width (optional)

  • FIELDNAME must be enclosed in parentheses, and can be one of:

    • depth_spacer - a number of spaces equal to the account's depth, or if MIN is specified, MIN * depth spaces.
    • account - the account's 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
--------------------
                 $-2  

List or tree mode

By default, or with -l/--flat, accounts are shown as a flat list with their full names visible, as in the examples above.

With -t/--tree, the account hierarchy is shown, with subaccounts' "leaf" names indented below their parent:

$ hledger -f examples/sample.journal balance
                 $-1  assets
                  $1    bank:saving
                 $-2    cash
                  $2  expenses
                  $1    food
                  $1    supplies
                 $-2  income
                 $-1    gifts
                 $-1    salary
                  $1  liabilities:debts
--------------------
                   0

Notes:

  • "Boring" accounts are combined with their subaccount for more compact output, unless --no-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
--------------------
                   0  

Dropping top-level accounts

You can also hide one or more top-level account name parts, using --drop NUM. This can be useful for hiding repetitive top-level account names:

$ hledger -f examples/sample.journal bal expenses --drop 1
                  $1  food
                  $1  supplies
--------------------
                  $2  

Showing declared accounts

With --declared, accounts which have been declared with an account directive will be included in the balance report, even if they have no transactions. (Since they will have a zero balance, you will also need -E/--empty to see them.)

More precisely, leaf declared accounts (with no subaccounts) will be included, since those are usually the more useful in reports.

The idea of this is to be able to see a useful "complete" balance report, even when you don't have transactions in all of your declared accounts yet.

Sorting by amount

With -S/--sort-amount, accounts with the largest (most positive) balances are shown first. Eg: hledger bal expenses -MAS shows your biggest averaged monthly expenses first. When more than one commodity is present, they will be sorted by the alphabetically earliest commodity first, and then by subsequent commodities (if an amount is missing a commodity, it is treated as 0).

Revenues and liability balances are typically negative, however, so -S shows these in reverse order. To work around this, you can add --invert to flip the signs. (Or, use one of the higher-level reports, which flip the sign automatically. Eg: hledger incomestatement -MAS).

Percentages

With -%/--percent, balance reports show each account's value expressed as a percentage of the (column) total.

Note it is not useful to calculate percentages if the amounts in a column have mixed signs. In this case, make a separate report for each sign, eg:

$ hledger bal -% amt:`>0`
$ hledger bal -% amt:`<0`

Similarly, if the amounts in a column have mixed commodities, convert them to one commodity with -B, -V, -X or --value, or make a separate report for each commodity:

$ hledger bal -% cur:\\$
$ hledger bal -% cur:€

Multi-period balance report

With a report interval (set by the -D/--daily, -W/--weekly, -M/--monthly, -Q/--quarterly, -Y/--yearly, or -p/--period flag), balance shows a tabular report, with columns representing successive time periods (and a title):

$ hledger -f examples/sample.journal bal --quarterly income expenses -E
Balance changes in 2008:

                   ||  2008q1  2008q2  2008q3  2008q4 
===================++=================================
 expenses:food     ||       0      $1       0       0 
 expenses:supplies ||       0      $1       0       0 
 income:gifts      ||       0     $-1       0       0 
 income:salary     ||     $-1       0       0       0 
-------------------++---------------------------------
                   ||     $-1      $1       0       0 

Notes:

  • The report's start/end dates will be expanded, if necessary, to fully encompass the displayed subperiods (so that the first and last subperiods have the same duration as the others).
  • Leading and trailing periods (columns) containing all zeroes are not shown, unless -E/--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:

hledger balance [CALCULATIONTYPE] [ACCUMULATIONTYPE] [VALUATIONTYPE] ...

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:

Valuation:>
Accumulation:v
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
2017-11-01
  income                   $-1950
  expenses:bus                $35
  expenses:food:groceries    $310
  expenses:food:dining        $42
  expenses:movies             $38
  assets:bank:checking

2017-12-01
  income                   $-2100
  expenses:bus                $53
  expenses:food:groceries    $380
  expenses:food:dining        $32
  expenses:gifts             $100
  assets:bank:checking

we can see a budget report like this:

$ hledger bal -M --budget
Budget performance in 2017-11-01..2017-12-31:

               ||                  Nov                   Dec 
===============++============================================
 <unbudgeted>  || $-425                 $-565                
 expenses      ||  $425 [ 99% of $430]   $565 [131% of $430] 
 expenses:bus  ||   $35 [117% of  $30]    $53 [177% of  $30] 
 expenses:food ||  $352 [ 88% of $400]   $412 [103% of $400] 
---------------++--------------------------------------------
               ||     0 [  0% of $430]      0 [  0% of $430] 

This is "goal-based budgeting"; you define goals for accounts and periods, often recurring, and hledger shows performance relative to the goals. This contrasts with "envelope budgeting", which is more detailed and strict - useful when cash is tight, but also quite a bit more work. https://plaintextaccounting.org/Budgeting has more on this topic.

Using the budget report

Historically this report has been confusing and fragile. hledger's version should be relatively robust and intuitive, but you may still find surprises. Here are more notes to help with learning and troubleshooting.

  • In the above example, expenses: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

2020-01-15
  expenses:food    $400
  assets:checking
$ hledger bal --budget expenses
Budget performance in 2020-01-15:

               ||         2020-01-15 
===============++====================
 <unbudgeted>  || $400               
 expenses:food ||    0 [ 0% of $500] 
---------------++--------------------
               || $400 [80% of $500] 

In this case, the budget goal transactions are generated on first days of of month (this can be seen with hledger print --forecast tag:generated expenses). Whereas the report period defaults to just the 15th day of january (this can be seen from the report table's column headings).

To fix this kind of thing, be more explicit about the report period (and/or the periodic rules' dates). In this case, adding -b 2020 does the trick.

Selecting budget goals

By default, the budget report uses all available periodic transaction rules to generate goals. This includes rules with a different report interval from your report. Eg if you have daily, weekly and monthly periodic rules, all of these will contribute to the goals in a monthly budget report.

You can select a subset of periodic rules by providing an argument to the --budget flag. --budget=DESCPAT will match all periodic rules whose description contains DESCPAT, a case-insensitive substring (not a regular expression or query). This means you can give your periodic rules descriptions (remember that two spaces are needed between period expression and description), and then select from multiple budgets defined in your journal.

Budgeting vs forecasting

--forecast and --budget both use the periodic transaction rules in the journal to generate temporary transactions for reporting purposes. However they are separate features - though you can use both at the same time if you want. Here are some differences between them:

--forecast--budget
is a general option; it enables forecasting with all 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:

-txtcsvhtmljsonsql
wideYYY
tallYYY
bareYYY
tidyY

Examples:

Wide layout

With many commodities, reports can be very wide:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=wide
Balance changes in 2012-01-01..2014-12-31:

                  ||                                          2012                                                     2013                                             2014                                                      Total 
==================++====================================================================================================================================================================================================================
 Assets:US:ETrade || 10.00 ITOT, 337.18 USD, 12.00 VEA, 106.00 VHT  70.00 GLD, 18.00 ITOT, -98.12 USD, 10.00 VEA, 18.00 VHT  -11.00 ITOT, 4881.44 USD, 14.00 VEA, 170.00 VHT  70.00 GLD, 17.00 ITOT, 5120.50 USD, 36.00 VEA, 294.00 VHT 
------------------++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                  || 10.00 ITOT, 337.18 USD, 12.00 VEA, 106.00 VHT  70.00 GLD, 18.00 ITOT, -98.12 USD, 10.00 VEA, 18.00 VHT  -11.00 ITOT, 4881.44 USD, 14.00 VEA, 170.00 VHT  70.00 GLD, 17.00 ITOT, 5120.50 USD, 36.00 VEA, 294.00 VHT 

A width limit reduces the width, but some commodities will be hidden:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=wide,32
Balance changes in 2012-01-01..2014-12-31:

                  ||                             2012                             2013                   2014                            Total 
==================++===========================================================================================================================
 Assets:US:ETrade || 10.00 ITOT, 337.18 USD, 2 more..  70.00 GLD, 18.00 ITOT, 3 more..  -11.00 ITOT, 3 more..  70.00 GLD, 17.00 ITOT, 3 more.. 
------------------++---------------------------------------------------------------------------------------------------------------------------
                  || 10.00 ITOT, 337.18 USD, 2 more..  70.00 GLD, 18.00 ITOT, 3 more..  -11.00 ITOT, 3 more..  70.00 GLD, 17.00 ITOT, 3 more.. 
Tall layout

Each commodity gets a new line (may be different in each column), and account names are repeated:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=tall
Balance changes in 2012-01-01..2014-12-31:

                  ||       2012        2013         2014        Total 
==================++==================================================
 Assets:US:ETrade || 10.00 ITOT   70.00 GLD  -11.00 ITOT    70.00 GLD 
 Assets:US:ETrade || 337.18 USD  18.00 ITOT  4881.44 USD   17.00 ITOT 
 Assets:US:ETrade ||  12.00 VEA  -98.12 USD    14.00 VEA  5120.50 USD 
 Assets:US:ETrade || 106.00 VHT   10.00 VEA   170.00 VHT    36.00 VEA 
 Assets:US:ETrade ||              18.00 VHT                294.00 VHT 
------------------++--------------------------------------------------
                  || 10.00 ITOT   70.00 GLD  -11.00 ITOT    70.00 GLD 
                  || 337.18 USD  18.00 ITOT  4881.44 USD   17.00 ITOT 
                  ||  12.00 VEA  -98.12 USD    14.00 VEA  5120.50 USD 
                  || 106.00 VHT   10.00 VEA   170.00 VHT    36.00 VEA 
                  ||              18.00 VHT                294.00 VHT 
Bare layout

Commodity symbols are kept in one column, each commodity has its own row, amounts are bare numbers, account names are repeated:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -T -Y --layout=bare
Balance changes in 2012-01-01..2014-12-31:

                  || Commodity    2012    2013     2014    Total 
==================++=============================================
 Assets:US:ETrade || GLD             0   70.00        0    70.00 
 Assets:US:ETrade || ITOT        10.00   18.00   -11.00    17.00 
 Assets:US:ETrade || USD        337.18  -98.12  4881.44  5120.50 
 Assets:US:ETrade || VEA         12.00   10.00    14.00    36.00 
 Assets:US:ETrade || VHT        106.00   18.00   170.00   294.00 
------------------++---------------------------------------------
                  || GLD             0   70.00        0    70.00 
                  || ITOT        10.00   18.00   -11.00    17.00 
                  || USD        337.18  -98.12  4881.44  5120.50 
                  || VEA         12.00   10.00    14.00    36.00 
                  || VHT        106.00   18.00   170.00   294.00 

Bare layout also affects CSV output, which is useful for producing data that is easier to consume, eg for making charts:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -O csv --layout=bare
"account","commodity","balance"
"Assets:US:ETrade","GLD","70.00"
"Assets:US:ETrade","ITOT","17.00"
"Assets:US:ETrade","USD","5120.50"
"Assets:US:ETrade","VEA","36.00"
"Assets:US:ETrade","VHT","294.00"
"total","GLD","70.00"
"total","ITOT","17.00"
"total","USD","5120.50"
"total","VEA","36.00"
"total","VHT","294.00"

Bare layout will sometimes display an extra row for the no-symbol commodity, because of zero amounts (hledger treats zeroes as commodity-less, usually). This can break hledger-bar confusingly (workaround: add a cur: query to exclude the no-symbol row).

Tidy layout

This produces normalised "tidy data" (see https://cran.r-project.org/web/packages/tidyr/vignettes/tidy-data.html) where every variable has its own column and each row represents a single data point. This is the easiest kind of data for other software to consume:

$ hledger -f examples/bcexample.hledger bal assets:us:etrade -3 -Y -O csv --layout=tidy
"account","period","start_date","end_date","commodity","value"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","GLD","0"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","ITOT","10.00"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","USD","337.18"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","VEA","12.00"
"Assets:US:ETrade","2012","2012-01-01","2012-12-31","VHT","106.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","GLD","70.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","ITOT","18.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","USD","-98.12"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","VEA","10.00"
"Assets:US:ETrade","2013","2013-01-01","2013-12-31","VHT","18.00"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","GLD","0"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","ITOT","-11.00"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","USD","4881.44"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","VEA","14.00"
"Assets:US:ETrade","2014","2014-01-01","2014-12-31","VHT","170.00"

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.

Also:

  • bal -M expenses -2 -SA
    Show monthly expenses summarised to depth 2 and sorted by average amount.

  • bal -M --budget expenses
    Show monthly expenses and budget goals.

  • bal -M --valuechange investments
    Show monthly change in market value of investment assets.

  • bal investments --valuechange -D date:lastweek amt:'>1000' -STA [--invert]
    Show top gainers [or losers] last week

balancesheet

(bs)

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.

Example:

$ hledger balancesheet
Balance Sheet 2008-12-31

                    || 2008-12-31 
====================++============
 Assets             ||            
--------------------++------------
 assets:bank:saving ||         $1 
 assets:cash        ||        $-2 
--------------------++------------
                    ||        $-1 
====================++============
 Liabilities        ||            
--------------------++------------
 liabilities:debts  ||        $-1 
--------------------++------------
                    ||        $-1 
====================++============
 Net:               ||          0 

This command is a higher-level variant of the balance command, and supports many of that command's features, such as multi-period reports. It is similar to hledger balance -H assets liabilities, but with smarter account detection, and liabilities displayed with their sign flipped.

This command also supports the output destination and output format options The output formats supported are txt, csv, tsv (Added in 1.32), html, and json.

balancesheetequity

(bse)

This command displays a balance sheet, showing historical ending balances of asset, liability and equity accounts. Amounts are shown with normal positive sign, as in conventional financial statements.

This report shows accounts declared with the Asset, Cash, Liability or Equity type (see account types). Or if no such accounts are declared, it shows top-level accounts named asset, liability or equity (case insensitive, plurals allowed) and their subaccounts.

Example:

$ hledger balancesheetequity
Balance Sheet With Equity 2008-12-31

                    || 2008-12-31 
====================++============
 Assets             ||            
--------------------++------------
 assets:bank:saving ||         $1 
 assets:cash        ||        $-2 
--------------------++------------
                    ||        $-1 
====================++============
 Liabilities        ||            
--------------------++------------
 liabilities:debts  ||        $-1 
--------------------++------------
                    ||        $-1 
====================++============
 Equity             ||            
--------------------++------------
--------------------++------------
                    ||          0 
====================++============
 Net:               ||          0 

This command is a higher-level variant of the balance command, and supports many of that command's features, such as multi-period reports. It is similar to hledger balance -H assets liabilities equity, but with smarter account detection, and liabilities/equity displayed with their sign flipped.

This report is the easiest way to see if the accounting equation (A+L+E = 0) is satisfied (after you have done a close --retain to merge revenues and expenses with equity, and perhaps added --infer-equity to balance your commodity conversions).

This command also supports the output destination and output format options The output formats supported are txt, csv, tsv, html, and json.

cashflow

(cf)

This command displays a (simple) cashflow statement, showing the inflows and outflows affecting "cash" (ie, liquid, easily convertible) assets. Amounts are shown with normal positive sign, as in conventional financial statements.

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:

^assets?(:.+)?:(cash|bank|che(ck|que?)(ing)?|savings?|currentcash)(:|$)

and their subaccounts.

An example cashflow report:

$ hledger cashflow
Cashflow Statement 2008

                    || 2008 
====================++======
 Cash flows         ||      
--------------------++------
 assets:bank:saving ||   $1 
 assets:cash        ||  $-2 
--------------------++------
                    ||  $-1 

This command is a higher-level variant of the balance command, and supports many of that command's features, such as multi-period reports. It is similar to hledger balance assets not:fixed not:investment not:receivable, but with smarter account detection.

This command also supports the output destination and output format options The output formats supported are txt, csv, tsv (Added in 1.32), html, and json.

check

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

(equity)

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.

codes

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.

Examples:

2022/1/1 (123) Supermarket   
 Food       $5.00
 Checking    

2022/1/2 (124) Post Office
 Postage    $8.32
 Checking

2022/1/3 Supermarket
 Food      $11.23
 Checking 

2022/1/4 (126) Post Office
 Postage    $3.21
 Checking
$ hledger codes
123
124
126
$ hledger codes -E
123
124

126

commodities

List all commodity/currency symbols used or declared in the journal.

demo

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.

Examples:

$ hledger demo               # list available demos
$ hledger demo 1             # play the first demo at default speed (2x)
$ hledger demo install -s4   # play the "install" demo at 4x speed

descriptions

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.

Example:

$ hledger descriptions
Store Name
Gas Station | Petrol
Person A

diff

Compares a particular account's transactions in two input files. It shows any transactions to this account which are in one file but not in the other.

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.

Examples:

$ hledger diff -f $LEDGER_FILE -f bank.csv assets:bank:giro 
These transactions are in the first file only:

2014/01/01 Opening Balances
    assets:bank:giro              EUR ...
    ...
    equity:opening balances       EUR -...

These transactions are in the second file only:

files

List all files included in the journal. With a REGEX argument, only file names matching the regular expression (case sensitive) are shown.

help

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

Examples

$ 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

import

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.

Date skipping

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,

  • It tries to read the latest date previously seen, from .latest.FILE in the same directory
  • Then it processes FILE, ignoring transactions on or before that date

And after a successful import, unless --dry-run was used, it updates the .latest.FILE(s) for next time. This is a simple system that works for most real-world CSV files; it assumes the following are true, or true enough:

  1. the name of the input file is stable across successive downloads
  2. new items always have the newest dates
  3. item dates are stable across downloads
  4. the order of same-date items is stable across downloads.

Tips:

  • To help ensure a stable file name, remember you can use a CSV rules file as an input file.

  • 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. (If it happens in old transactions, that's harmless.)

Note this is just one kind of "deduplication": not reprocessing the same dates across successive runs. import doesn't detect other kinds of duplication, such as the same transaction appearing multiple times within a single run, or a new transaction that looks identical to a transaction already in the journal. (Because these can happen legitimately in real-world data.)

Here's a situation where you need to run import with care: say you download but forget to import bank.1.csv, and a week later you download bank.2.csv with some overlapping data. You should not process both of these as a single import (hledger import bank.1.csv bank.2.csv), because the overlapping transactions would not be deduplicated. Instead, import one file at a time, using the same filename each time:

$ mv bank.1.csv bank.csv; hledger import bank.csv
$ mv bank.2.csv bank.csv; hledger import bank.csv

Normally you don't need to think about .latest.* files, but you can create or modify them to catch up to a certain date, or delete them to mark all transactions as new. Their format is a single ISO-format YYYY-MM-DD date, optionally repeated on multiple lines, meaning "I have seen the transactions before this date, and this many of them on this 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.)

Import and commodity styles

Amounts in entries added by import will be formatted according to the journal's canonical commodity styles, as declared by commodity directives or inferred from the journal's amounts.

Related: CSV > Amount decimal places.

incomestatement

(is)

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.

Example:

$ hledger incomestatement
Income Statement 2008

                   || 2008 
===================++======
 Revenues          ||      
-------------------++------
 income:gifts      ||   $1 
 income:salary     ||   $1 
-------------------++------
                   ||   $2 
===================++======
 Expenses          ||      
-------------------++------
 expenses:food     ||   $1 
 expenses:supplies ||   $1 
-------------------++------
                   ||   $2 
===================++======
 Net:              ||    0 

This command is a higher-level variant of the balance command, and supports many of that command's features, such as multi-period reports. It is similar to hledger balance '(revenues|income)' expenses, but with smarter account detection, and revenues/income displayed with their sign flipped.

This command also supports the output destination and output format options The output formats supported are txt, csv, tsv (Added in 1.32), html, and json.

notes

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

Example:

$ hledger notes
Petrol
Snacks

payees

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.

Example:

$ hledger payees
Store Name
Gas Station
Person A

prices

Print the market prices declared with P directives. With --infer-market-prices, also show any additional prices inferred from costs. With --show-reverse, also show additional prices inferred by reversing known prices.

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.

Eg:

$ hledger print -f examples/sample.journal date:200806
2008/06/01 gift
    assets:bank:checking            $1
    income:gifts                   $-1

2008/06/02 save
    assets:bank:saving              $1
    assets:bank:checking           $-1

2008/06/03 * eat & shop
    expenses:food                $1
    expenses:supplies            $1
    assets:cash                 $-2

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
"txnidx","date","date2","status","code","description","comment","account","amount","commodity","credit","debit","posting-status","posting-comment"
"1","2008/01/01","","","","income","","assets:bank:checking","1","$","","1","",""
"1","2008/01/01","","","","income","","income:salary","-1","$","1","","",""
"2","2008/06/01","","","","gift","","assets:bank:checking","1","$","","1","",""
"2","2008/06/01","","","","gift","","income:gifts","-1","$","1","","",""
"3","2008/06/02","","","","save","","assets:bank:saving","1","$","","1","",""
"3","2008/06/02","","","","save","","assets:bank:checking","-1","$","1","","",""
"4","2008/06/03","","*","","eat & shop","","expenses:food","1","$","","1","",""
"4","2008/06/03","","*","","eat & shop","","expenses:supplies","1","$","","1","",""
"4","2008/06/03","","*","","eat & shop","","assets:cash","-2","$","2","","",""
"5","2008/12/31","","*","","pay off","","liabilities:debts","1","$","","1","",""
"5","2008/12/31","","*","","pay off","","assets:bank:checking","-1","$","1","","",""
  • There is one CSV record per posting, with the parent transaction's fields repeated.
  • The "txnidx" (transaction index) field shows which postings belong to the same transaction. (This number might change if transactions are reordered within the file, files are parsed/included in a different order, etc.)
  • The amount is separated into "commodity" (the symbol) and "amount" (numeric quantity) fields.
  • The numeric amount is repeated in either the "credit" or "debit" column, for convenience. (Those names are not accurate in the accounting sense; it just puts negative amounts under credit and zero or greater amounts under debit.)

register

(reg)

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.

rewrite

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.

Examples:

$ hledger-rewrite.hs ^income --add-posting '(liabilities:tax)  *.33  ; income tax' --add-posting '(reserve:gifts)  $100'
$ hledger-rewrite.hs expenses:gifts --add-posting '(reserve:gifts)  *-1"'
$ hledger-rewrite.hs -f rewrites.hledger

rewrites.hledger may consist of entries like:

= ^income amt:<0 date:2017
  (liabilities:tax)  *0.33  ; tax on income
  (reserve:grocery)  *0.25  ; reserve 25% for grocery
  (reserve:)  *0.25  ; reserve 25% for grocery

Note the single quotes to protect the dollar sign from bash, and the two spaces between account and amount.

More:

$ hledger rewrite -- [QUERY]        --add-posting "ACCT  AMTEXPR" ...
$ hledger rewrite -- ^income        --add-posting '(liabilities:tax)  *.33'
$ hledger rewrite -- expenses:gifts --add-posting '(budget:gifts)  *-1"'
$ hledger rewrite -- ^income        --add-posting '(budget:foreign currency)  *0.25 JPY; diversify'

Argument for --add-posting option is a usual posting of transaction with an exception for amount specification. More precisely, you can use '*' (star symbol) before the amount to indicate that that this is a factor for an amount of original matched posting. If the amount includes a commodity name, the new posting amount will be in the new commodity; otherwise, it will be in the matched posting amount's commodity.

Re-write rules in a file

During the run this tool will execute so called "Automated Transactions" found in any journal it process. I.e instead of specifying this operations in command line you can put them in a journal file.

$ rewrite-rules.journal

Make contents look like this:

= ^income
    (liabilities:tax)  *.33

= expenses:gifts
    budget:gifts  *-1
    assets:budget  *1

Note that '=' (equality symbol) that is used instead of date in transactions you usually write. It indicates the query by which you want to match the posting to add new ones.

$ hledger rewrite -- -f input.journal -f rewrite-rules.journal > rewritten-tidy-output.journal

This is something similar to the commands pipeline:

$ hledger rewrite -- -f input.journal '^income' --add-posting '(liabilities:tax)  *.33' \
  | hledger rewrite -- -f - expenses:gifts      --add-posting 'budget:gifts  *-1'       \
                                                --add-posting 'assets:budget  *1'       \
  > rewritten-tidy-output.journal

It is important to understand that relative order of such entries in journal is important. You can re-use result of previously added postings.

Diff output format

To use this tool for batch modification of your journal files you may find useful output in form of unified diff.

$ hledger rewrite -- --diff -f examples/sample.journal '^income' --add-posting '(liabilities:tax)  *.33'

Output might look like:

--- /tmp/examples/sample.journal
+++ /tmp/examples/sample.journal
@@ -18,3 +18,4 @@
 2008/01/01 income
-    assets:bank:checking  $1
+    assets:bank:checking            $1
     income:salary
+    (liabilities:tax)                0
@@ -22,3 +23,4 @@
 2008/06/01 gift
-    assets:bank:checking  $1
+    assets:bank:checking            $1
     income:gifts
+    (liabilities:tax)                0

If you'll pass this through patch tool you'll get transactions containing the posting that matches your query be updated. Note that multiple files might be update according to list of input files specified via --file options and include directives inside of these files.

Be careful. Whole transaction being re-formatted in a style of output from hledger print.

See also:

https://github.com/simonmichael/hledger/issues/99

rewrite vs. print --auto

This command predates print --auto, and currently does much the same thing, but with these differences:

  • with multiple files, rewrite lets rules in any file affect all other files. print --auto uses standard directive scoping; rules affect only child files.

  • rewrite's query limits which transactions can be rewritten; all are printed. print --auto's query limits which transactions are printed.

  • rewrite applies rules specified on command line or in the journal. print --auto applies rules specified in the journal.

roi

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.

Examples:

Spaces and special characters in --inv and --pnl

Note that --inv and --pnl's argument is a query, and queries could have several space-separated terms (see QUERIES).

To indicate that all search terms form single command-line argument, you will need to put them in quotes (see Special characters):

$ hledger roi --inv 'term1 term2 term3 ...'

If any query terms contain spaces themselves, you will need an extra level of nested quoting, eg:

$ hledger roi --inv="'Assets:Test 1'" --pnl="'Equity:Unrealized Profit and Loss'"

Semantics of --inv and --pnl

Query supplied to --inv has to match all transactions that are related to your investment. Transactions not matching --inv will be ignored.

In these transactions, ROI will conside postings that match --inv to be "investment postings" and other postings (not matching --inv) will be sorted into two categories: "cash flow" and "profit and loss", as ROI needs to know which part of the investment value is your contributions and which is due to the return on investment.

  • "Cash flow" is depositing or withdrawing money, buying or selling assets, or otherwise converting between your investment commodity and any other commodity. Example:

    2019-01-01 Investing in Snake Oil
      assets:cash          -$100
      investment:snake oil
    
    2020-01-01 Selling my Snake Oil
      assets:cash           $10
      investment:snake oil  = 0
    
  • "Profit and loss" is change in the value of your investment:

    2019-06-01 Snake Oil falls in value
      investment:snake oil  = $57
      equity:unrealized profit or loss
    

All non-investment postings are assumed to be "cash flow", unless they match --pnl query. Changes in value of your investment due to "profit and loss" postings will be considered as part of your investment return.

Example: if you use --inv snake --pnl equity:unrealized, then postings in the example below would be classifed as:

2019-01-01 Snake Oil #1
  assets:cash          -$100   ; cash flow posting
  investment:snake oil         ; investment posting

2019-03-01 Snake Oil #2
  equity:unrealized pnl  -$100 ; profit and loss posting
  snake oil                    ; investment posting

2019-07-01 Snake Oil #3
  equity:unrealized pnl        ; profit and loss posting
  cash          -$100          ; cash flow posting
  snake oil     $50            ; investment posting

IRR and TWR explained

"ROI" stands for "return on investment". Traditionally this was computed as a difference between current value of investment and its initial value, expressed in percentage of the initial value.

However, this approach is only practical in simple cases, where investments receives no in-flows or out-flows of money, and where rate of growth is fixed over time. For more complex scenarios you need different ways to compute rate of return, and this command implements two of them: IRR and TWR.

Internal rate of return, or "IRR" (also called "money-weighted rate of return") takes into account effects of in-flows and out-flows, and the time between them. Investment at a particular fixed interest rate is going to give you more interest than the same amount invested at the same interest rate, but made later in time. If you are withdrawing from your investment, your future gains would be smaller (in absolute numbers), and will be a smaller percentage of your initial investment, so your IRR will be smaller. And if you are adding to your investment, you will receive bigger absolute gains, which will be a bigger percentage of your initial investment, so your IRR will be larger.

As mentioned before, in-flows and out-flows would be any cash that you personally put in or withdraw, and for the "roi" command, these are the postings that match the query in the--inv argument and NOT match the query in the--pnl argument.

If you manually record changes in the value of your investment as transactions that balance them against "profit and loss" (or "unrealized gains") account or use price directives, then in order for IRR to compute the precise effect of your in-flows and out-flows on the rate of return, you will need to record the value of your investement on or close to the days when in- or out-flows occur.

In technical terms, IRR uses the same approach as computation of net present value, and tries to find a discount rate that makes net present value of all the cash flows of your investment to add up to zero. This could be hard to wrap your head around, especially if you haven't done discounted cash flow analysis before. Implementation of IRR in hledger should produce results that match the =XIRR formula in Excel.

Second way to compute rate of return that roi command implements is called "time-weighted rate of return" or "TWR". Like IRR, it will account for the effect of your in-flows and out-flows, but unlike IRR it will try to compute the true rate of return of the underlying asset, compensating for the effect that deposits and withdrawas have on the apparent rate of growth of your investment.

TWR represents your investment as an imaginary "unit fund" where in-flows/ out-flows lead to buying or selling "units" of your investment and changes in its value change the value of "investment unit". Change in "unit price" over the reporting period gives you rate of return of your investment, and make TWR less sensitive than IRR to the effects of cash in-flows and out-flows.

References:

stats

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.

Example:

$ hledger stats -f examples/1ktxns-1kaccts.journal 
Main file           : .../1ktxns-1kaccts.journal
Included files      : 0
Txns span           : 2000-01-01 to 2002-09-27 (1000 days)
Last txn            : 2002-09-26 (7827 days ago)
Txns                : 1000 (1.0 per day)
Txns last 30 days   : 0 (0.0 per day)
Txns last 7 days    : 0 (0.0 per day)
Payees/descriptions : 1000
Accounts            : 1000 (depth 10)
Commodities         : 26
Market prices       : 1000
Runtime stats       : 0.12 s elapsed, 8266 txns/s, 4 MB live, 16 MB alloc

This command supports the -o/--output-file option (but not -O/--output-format).

tags

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.

test

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

PART 5: COMMON TASKS

Here are some quick examples of how to do some basic tasks with hledger.

Getting help

Here's how to list commands and view options and command docs:

$ hledger                # show available commands
$ hledger --help         # show common options
$ hledger CMD --help     # show CMD's options, common options and CMD's documentation

You can also view your hledger version's manual in several formats by using the help command. Eg:

$ hledger help           # show the hledger manual with info, man or $PAGER (best available)
$ hledger help journal   # show the journal topic in the hledger manual
$ hledger help --help    # find out more about the help command

To view manuals and introductory docs on the web, visit https://hledger.org. Chat and mail list support and discussion archives can be found at https://hledger.org/support.

Constructing command lines

hledger has a flexible command line interface. We strive to keep it simple and ergonomic, but if you run into one of the sharp edges described in OPTIONS, here are some tips that might help:

  • command-specific options must go after the command (it's fine to put common options there too: hledger CMD OPTS ARGS)
  • 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 hide regular expression metacharacters from the shell
  • to see how a misbehaving command line is being parsed, add --debug=2.

Starting a journal file

hledger looks for your accounting data in a journal file, $HOME/.hledger.journal by default:

$ hledger stats
The hledger journal file "/Users/simon/.hledger.journal" was not found.
Please create it first, eg with "hledger add" or a text editor.
Or, specify an existing journal file with -f or LEDGER_FILE.

You can override this by setting the LEDGER_FILE environment variable (see below). It's a good practice to keep this important file under version control, and to start a new file each year. So you could do something like this:

$ mkdir ~/finance
$ cd ~/finance
$ git init
Initialized empty Git repository in /Users/simon/finance/.git/
$ touch 2023.journal
$ echo "export LEDGER_FILE=$HOME/finance/2023.journal" >> ~/.profile
$ source ~/.profile
$ hledger stats
Main file                : /Users/simon/finance/2023.journal
Included files           : 
Transactions span        :  to  (0 days)
Last transaction         : none
Transactions             : 0 (0.0 per day)
Transactions last 30 days: 0 (0.0 per day)
Transactions last 7 days : 0 (0.0 per day)
Payees/descriptions      : 0
Accounts                 : 0 (depth 0)
Commodities              : 0 ()
Market prices            : 0 ()

Setting LEDGER_FILE

How to set LEDGER_FILE permanently depends on your setup:

On unix and mac, running these commands in the terminal will work for many people; adapt as needed:

$ echo 'export LEDGER_FILE=~/finance/2023.journal' >> ~/.profile
$ source ~/.profile

When correctly configured, in a new terminal window env | grep LEDGER_FILE will show your file, and so will hledger files.

On mac, this additional step might be helpful for GUI applications (like Emacs started from the dock): add an entry to ~/.MacOSX/environment.plist like

{
  "LEDGER_FILE" : "~/finance/2023.journal"
}

and then run killall Dock in a terminal window (or restart the machine).

On Windows, see https://www.java.com/en/download/help/path.html, or try running these commands in a powershell window (let us know if it persists across a reboot, and if you need to be an Administrator):

> CD
> MKDIR finance
> SETX LEDGER_FILE "C:\Users\USERNAME\finance\2023.journal"

Setting opening balances

Pick a starting date for which you can look up the balances of some real-world assets (bank accounts, wallet..) and liabilities (credit cards..).

To avoid a lot of data entry, you may want to start with just one or two accounts, like your checking account or cash wallet; and pick a recent starting date, like today or the start of the week. You can always come back later and add more accounts and older transactions, eg going back to january 1st.

Add an opening balances transaction to the journal, declaring the balances on this date. Here are two ways to do it:

  • The first way: open the journal in any text editor and save an entry like this:

    2023-01-01 * opening balances
        assets:bank:checking                $1000   = $1000
        assets:bank:savings                 $2000   = $2000
        assets:cash                          $100   = $100
        liabilities:creditcard               $-50   = $-50
        equity:opening/closing balances
    

    These are start-of-day balances, ie whatever was in the account at the end of the previous day.

    The * after the date is an optional status flag. Here it means "cleared & confirmed".

    The currency symbols are optional, but usually a good idea as you'll be dealing with multiple currencies sooner or later.

    The = amounts are optional balance assertions, providing extra error checking.

  • The second way: run hledger add and follow the prompts to record a similar transaction:

    $ hledger add
    Adding transactions to journal file /Users/simon/finance/2023.journal
    Any command line arguments will be used as defaults.
    Use tab key to complete, readline keys to edit, enter to accept defaults.
    An optional (CODE) may follow transaction dates.
    An optional ; COMMENT may follow descriptions or amounts.
    If you make a mistake, enter < at any prompt to go one step backward.
    To end a transaction, enter . when prompted.
    To quit, enter . at a date prompt or press control-d or control-c.
    Date [2023-02-07]: 2023-01-01
    Description: * opening balances
    Account 1: assets:bank:checking
    Amount  1: $1000
    Account 2: assets:bank:savings
    Amount  2 [$-1000]: $2000
    Account 3: assets:cash
    Amount  3 [$-3000]: $100
    Account 4: liabilities:creditcard
    Amount  4 [$-3100]: $-50
    Account 5: equity:opening/closing balances
    Amount  5 [$-3050]: 
    Account 6 (or . or enter to finish this transaction): .
    2023-01-01 * opening balances
        assets:bank:checking                      $1000
        assets:bank:savings                       $2000
        assets:cash                                $100
        liabilities:creditcard                     $-50
        equity:opening/closing balances          $-3050
    
    Save this transaction to the journal ? [y]: 
    Saved.
    Starting the next transaction (. or ctrl-D/ctrl-C to quit)
    Date [2023-01-01]: .
    

If you're using version control, this could be a good time to commit the journal. Eg:

$ git commit -m 'initial balances' 2023.journal

Recording transactions

As you spend or receive money, you can record these transactions using one of the methods above (text editor, hledger add) or by using the hledger-iadd or hledger-web add-ons, or by using the import command to convert CSV data downloaded from your bank.

Here are some simple transactions, see the hledger_journal(5) manual and hledger.org for more ideas:

2023/1/10 * gift received
  assets:cash   $20
  income:gifts

2023.1.12 * farmers market
  expenses:food    $13
  assets:cash

2023-01-15 paycheck
  income:salary
  assets:bank:checking    $1000

Reconciling

Periodically you should reconcile - compare your hledger-reported balances against external sources of truth, like bank statements or your bank's website - to be sure that your ledger accurately represents the real-world balances (and, that the real-world institutions have not made a mistake!). This gets easy and fast with (1) practice and (2) frequency. If you do it daily, it can take 2-10 minutes. If you let it pile up, expect it to take longer as you hunt down errors and discrepancies.

A typical workflow:

  1. Reconcile cash. Count what's in your wallet. Compare with what hledger reports (hledger bal cash). If they are different, try to remember the missing transaction, or look for the error in the already-recorded transactions. A register report can be helpful (hledger reg cash). If you can't find the error, add an adjustment transaction. Eg if you have $105 after the above, and can't explain the missing $2, it could be:

    2023-01-16 * adjust cash
        assets:cash    $-2 = $105
        expenses:misc
    
  2. Reconcile checking. Log in to your bank's website. Compare today's (cleared) balance with hledger's cleared balance (hledger bal checking -C). If they are different, track down the error or record the missing transaction(s) or add an adjustment transaction, similar to the above. Unlike the cash case, you can usually compare the transaction history and running balance from your bank with the one reported by hledger reg checking -C. This will be easier if you generally record transaction dates quite similar to your bank's clearing dates.

  3. Repeat for other asset/liability accounts.

Tip: instead of the register command, use hledger-ui to see a live-updating register while you edit the journal: hledger-ui --watch --register checking -C

After reconciling, it could be a good time to mark the reconciled transactions' status as "cleared and confirmed", if you want to track that, by adding the * marker. Eg in the paycheck transaction above, insert * between 2023-01-15 and paycheck

If you're using version control, this can be another good time to commit:

$ git commit -m 'txns' 2023.journal

Reporting

Here are some basic reports.

Show all transactions:

$ hledger print
2023-01-01 * opening balances
    assets:bank:checking                      $1000
    assets:bank:savings                       $2000
    assets:cash                                $100
    liabilities:creditcard                     $-50
    equity:opening/closing balances          $-3050

2023-01-10 * gift received
    assets:cash              $20
    income:gifts

2023-01-12 * farmers market
    expenses:food             $13
    assets:cash

2023-01-15 * paycheck
    income:salary
    assets:bank:checking           $1000

2023-01-16 * adjust cash
    assets:cash               $-2 = $105
    expenses:misc

Show account names, and their hierarchy:

$ hledger accounts --tree
assets
  bank
    checking
    savings
  cash
equity
  opening/closing balances
expenses
  food
  misc
income
  gifts
  salary
liabilities
  creditcard

Show all account totals:

$ hledger balance
               $4105  assets
               $4000    bank
               $2000      checking
               $2000      savings
                $105    cash
              $-3050  equity:opening/closing balances
                 $15  expenses
                 $13    food
                  $2    misc
              $-1020  income
                $-20    gifts
              $-1000    salary
                $-50  liabilities:creditcard
--------------------
                   0

Show only asset and liability balances, as a flat list, limited to depth 2:

$ hledger bal assets liabilities -2
               $4000  assets:bank
                $105  assets:cash
                $-50  liabilities:creditcard
--------------------
               $4055

Show the same thing without negative numbers, formatted as a simple balance sheet:

$ hledger bs -2
Balance Sheet 2023-01-16

                        || 2023-01-16 
========================++============
 Assets                 ||            
------------------------++------------
 assets:bank            ||      $4000 
 assets:cash            ||       $105 
------------------------++------------
                        ||      $4105 
========================++============
 Liabilities            ||            
------------------------++------------
 liabilities:creditcard ||        $50 
------------------------++------------
                        ||        $50 
========================++============
 Net:                   ||      $4055 

The final total is your "net worth" on the end date. (Or use bse for a full balance sheet with equity.)

Show income and expense totals, formatted as an income statement:

hledger is 
Income Statement 2023-01-01-2023-01-16

               || 2023-01-01-2023-01-16 
===============++=======================
 Revenues      ||                       
---------------++-----------------------
 income:gifts  ||                   $20 
 income:salary ||                 $1000 
---------------++-----------------------
               ||                 $1020 
===============++=======================
 Expenses      ||                       
---------------++-----------------------
 expenses:food ||                   $13 
 expenses:misc ||                    $2 
---------------++-----------------------
               ||                   $15 
===============++=======================
 Net:          ||                 $1005 

The final total is your net income during this period.

Show transactions affecting your wallet, with running total:

$ hledger register cash
2023-01-01 opening balances     assets:cash                   $100          $100
2023-01-10 gift received        assets:cash                    $20          $120
2023-01-12 farmers market       assets:cash                   $-13          $107
2023-01-16 adjust cash          assets:cash                    $-2          $105

Show weekly posting counts as a bar chart:

$ hledger activity -W
2019-12-30 *****
2023-01-06 ****
2023-01-13 ****

Migrating to a new file

At the end of the year, you may want to continue your journal in a new file, so that old transactions don't slow down or clutter your reports, and to help ensure the integrity of your accounting history. See the close command.

If using version control, don't forget to git add the new file.

BUGS

We welcome bug reports in the hledger issue tracker (shortcut: http://bugs.hledger.org), or on the #hledger chat or hledger mail list (https://hledger.org/support).

Some known issues and limitations:

The need to precede add-on command options with -- when invoked from hledger is awkward. (See Command options, Constructing command lines.)

A UTF-8-aware system locale must be configured to work with non-ascii data. (See Unicode characters, Troubleshooting.)

On Microsoft Windows, depending whether you are running in a CMD window or a Cygwin/MSYS/Mintty window and how you installed hledger, non-ascii characters and colours may not be supported, and the tab key may not be supported by hledger add. (Running in a WSL window should resolve these.)

When processing large data files, hledger uses more memory than Ledger.

Troubleshooting

Here are some common issues you might encounter when you run hledger, and how to resolve them (and remember also you can usually get quick Support):

PATH issues: I get an error like "No command 'hledger' found"
Depending how you installed hledger, the executables may not be in your shell's PATH. Eg on unix systems, stack installs hledger in ~/.local/bin and cabal installs it in ~/.cabal/bin. You may need to add one of these directories to your shell's PATH, and/or open a new terminal window.

LEDGER_FILE issues: I configured LEDGER_FILE but hledger is not using it

  • LEDGER_FILE should be a real environment variable, not just a shell variable. Eg on unix, the command env | grep LEDGER_FILE should show it. You may need to use export (see https://stackoverflow.com/a/7411509).
  • You may need to force your shell to see the new configuration. A simple way is to close your terminal window and open a new one.

LANG issues: I get errors like "Illegal byte sequence" or "Invalid or incomplete multibyte or wide character" or "commitAndReleaseBuffer: invalid argument (invalid character)"
Programs compiled with GHC (hledger, haskell build tools, etc.) need the system locale to be UTF-8-aware, or they will fail when they encounter non-ascii characters. To fix it, set the LANG environment variable to a locale which supports UTF-8 and which is installed on your system.

On unix, locale -a lists the installed locales. Look for one which mentions utf8, UTF-8 or similar. Some examples: C.UTF-8, en_US.utf-8, fr_FR.utf8. If necessary, use your system package manager to install one. Then select it by setting the LANG environment variable. Note, exact spelling and capitalisation of the locale name may be important: Here's one common way to configure this permanently for your shell:

$ echo "export LANG=en_US.utf8" >>~/.profile
# close and re-open terminal window

If you are using Nix (not NixOS) for GHC and Hledger, you might need to set the LOCALE_ARCHIVE variable:

$ echo "export LOCALE_ARCHIVE=${glibcLocales}/lib/locale/locale-archive" >>~/.profile
# close and re-open terminal window

COMPATIBILITY ISSUES: hledger gives an error with my Ledger file
Not all of Ledger's journal file syntax or feature set is supported. See hledger and Ledger for full details.

hledger-ui

NAME

hledger-ui - robust, friendly plain text accounting (TUI version)

SYNOPSIS

hledger-ui [OPTS] [QUERYARGS]
hledger ui -- [OPTS] [QUERYARGS]

DESCRIPTION

This manual is for hledger's terminal interface, version 1.33.99. See also the hledger manual for common concepts and file formats.

hledger is a robust, user-friendly, cross-platform set of programs for tracking money, time, or any other commodity, using double-entry accounting and a simple, editable file format. hledger is inspired by and largely compatible with ledger(1), and largely interconvertible with beancount(1).

hledger-ui is hledger's terminal interface, providing an efficient full-window text UI for viewing accounts and transactions, and some limited data entry capability. It is easier than hledger's command-line interface, and sometimes quicker and more convenient than the web interface.

Like hledger, it reads from (and appends to) a journal file specified by the LEDGER_FILE environment variable (defaulting to $HOME/.hledger.journal); or you can specify files with -f options. It can also read timeclock files, timedot files, or any CSV/SSV/TSV file with a date field. (See hledger(1) -> Input for details.)

Unlike hledger, hledger-ui hides all future-dated transactions by default. They can be revealed, along with any rule-generated periodic transactions, by pressing the F key (or starting with --forecast) to enable "forecast mode".

OPTIONS

Any QUERYARGS are interpreted as a hledger search query which filters the data.

hledger-ui provides the following options:

-w --watch : watch for data and date changes and reload automatically

--theme=default|terminal|greenterm|dark : use this custom display theme

--menu : start in the menu screen

--cash : start in the cash accounts screen

--bs : start in the balance sheet accounts screen

--is : start in the income statement accounts screen

--all : start in the all accounts screen

--register=ACCTREGEX : start in the (first) matched account's register screen

--change : show period balances (changes) at startup instead of historical balances

-l --flat : show accounts as a flat list (default)

-t --tree : show accounts as a tree

hledger-ui also supports many of hledger's general options (and the hledger manual's command line tips also apply here):

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.

MOUSE

In most modern terminals, you can navigate through the screens with a mouse or touchpad:

  • Use mouse wheel or trackpad to scroll up and down
  • Click on list items to go deeper
  • Click on the left margin (column 0) to go back.

KEYS

Keyboard gives more control.

? shows a help dialog listing all keys. (Some of these also appear in the quick help at the bottom of each screen.) Press ? again (or ESCAPE, or LEFT, or q) to close it. The following keys work on most screens:

The cursor keys navigate: RIGHT or ENTER goes deeper, LEFT returns to the previous screen, UP/DOWN/PGUP/PGDN/HOME/END move up and down through lists. Emacs-style (CTRL-p/CTRL-n/CTRL-f/CTRL-b) and VI-style (k,j,l,h) movement keys are also supported. A tip: movement speed is limited by your keyboard repeat rate, to move faster you may want to adjust it. (If you're on a mac, the karabiner app is one way to do that.)

With shift pressed, the cursor keys adjust the report period, limiting the transactions to be shown (by default, all are shown). SHIFT-DOWN/UP steps downward and upward through these standard report period durations: year, quarter, month, week, day. Then, SHIFT-LEFT/RIGHT moves to the previous/next period. T sets the report period to today. With the -w/--watch option, when viewing a "current" period (the current day, week, month, quarter, or year), the period will move automatically to track the current date. To set a non-standard period, you can use / and a date: query.

(Mac users: SHIFT-DOWN/UP keys do not work by default in Terminal, as of MacOS Monterey. You can configure them as follows: open Terminal, press CMD-comma to open preferences, click Profiles, select your current terminal profile on the left, click Keyboard on the right, click + and add this for Shift-Down: \033[1;2B, click + and add this for Shift-Up: \033[1;2A. Press the Escape key to enter the \033 part, you can't type it directly.)

/ lets you set a general filter query limiting the data shown, using the same query terms as in hledger and hledger-web. While editing the query, you can use CTRL-a/e/d/k, BS, cursor keys; press ENTER to set it, or ESCAPEto cancel. There are also keys for quickly adjusting some common filters like account depth and transaction status (see below). BACKSPACE or DELETE removes all filters, showing all transactions.

As mentioned above, by default hledger-ui hides future transactions - both ordinary transactions recorded in the journal, and periodic transactions generated by rule. F toggles forecast mode, in which future/forecasted transactions are shown.

ESCAPE resets the UI state and jumps back to the top screen, restoring the app's initial state at startup. Or, it cancels minibuffer data entry or the help dialog.

CTRL-l redraws the screen and centers the selection if possible (selections near the top won't be centered, since we don't scroll above the top).

g reloads from the data file(s) and updates the current screen and any previous screens. (With large files, this could cause a noticeable pause.)

I toggles balance assertion checking. Disabling balance assertions temporarily can be useful for troubleshooting.

a runs command-line hledger's add command, and reloads the updated file. This allows some basic data entry.

A is like a, but runs the hledger-iadd tool, which provides a terminal interface. This key will be available if hledger-iadd is installed in $path.

E runs $HLEDGER_UI_EDITOR, or $EDITOR, or a default (emacsclient -a "" -nw) on the journal file. With some editors (emacs, vi), the cursor will be positioned at the current transaction when invoked from the register and transaction screens, and at the error location (if possible) when invoked from the error screen.

B toggles cost mode, showing amounts converted to their cost's commodity (see hledger manual > Cost reporting.

V toggles value mode, showing amounts converted to their market value (see hledger manual > Valuation flag). More specifically,

  1. By default, the V key toggles showing end value (--value=end) on or off. The valuation date will be the report end date if specified, otherwise today.

  2. If you started hledger-ui with some other valuation (such as --value=then,EUR), the V key toggles that off or on.

Cost/value tips: - When showing end value, you can change the report end date without restarting, by pressing / and adding a query like date:..YYYY-MM-DD. - Either cost mode, or value mode, can be active, but not both at once. Cost mode takes precedence. - There's not yet any visual indicator that cost or value mode is active, other than the amount values.

q quits the application.

Additional screen-specific keys are described below.

SCREENS

At startup, hledger-ui shows a menu screen by default. From here you can navigate to other screens using the cursor keys: UP/DOWN to select, RIGHT to move to the selected screen, LEFT to return to the previous screen. Or you can use ESC to return directly to the top menu screen.

You can also use a command line flag to specific a different startup screen (--cs, --bs, --is, --all, or --register=ACCT).

This is the top-most screen. From here you can navigate to several screens listing accounts of various types. Note some of these may not show anything until you have configured account types.

Cash accounts

This screen shows "cash" (ie, liquid asset) accounts (like hledger balancesheet type:c). It always shows balances (historical ending balances on the date shown in the title line).

Balance sheet accounts

This screen shows asset, liability and equity accounts (like hledger balancesheetequity). It always shows balances.

Income statement accounts

This screen shows revenue and expense accounts (like hledger incomestatement). It always shows changes (balance changes in the period shown in the title line).

All accounts

This screen shows all accounts in your journal (unless filtered by a query; like hledger balance). It shows balances by default; you can toggle showing changes with the H key.

Register

This screen shows the transactions affecting a particular account. Each line represents one transaction, and shows:

  • the other account(s) involved, in abbreviated form. (If there are both real and virtual postings, it shows only the accounts affected by real postings.)

  • the overall change to the current account's balance; positive for an inflow to this account, negative for an outflow.

  • the running total after the transaction. With the H key you can toggle between

    • the period total, which is from just the transactions displayed
    • or the historical total, which includes any undisplayed transactions before the start of the report period (and matching the filter query if any). This will be the running historical balance (what you would see on a bank's website, eg) if not disturbed by a query.

Note, this screen combines each transaction's in-period postings to a single line item, dated with the earliest in-period transaction or posting date (like hledger's aregister). So custom posting dates can cause the running balance to be temporarily inaccurate. (See hledger manual > aregister and posting dates.)

Transactions affecting this account's subaccounts will be included in the register if the accounts screen is in tree mode, or if it's in list mode but this account has subaccounts which are not shown due to a depth limit. In other words, the register always shows the transactions contributing to the balance shown on the accounts screen. Tree mode/list mode can be toggled with t here also.

U toggles filtering by unmarked status, showing or hiding unmarked transactions. Similarly, P toggles pending transactions, and C toggles cleared transactions. (By default, transactions with all statuses are shown; if you activate one or two status filters, only those transactions are shown; and if you activate all three, the filter is removed.)

R toggles real mode, in which virtual postings are ignored.

z toggles nonzero mode, in which only transactions posting a nonzero change are shown (hledger-ui shows zero items by default, unlike command-line hledger).

Press RIGHT to view the selected transaction in detail.

Transaction

This screen shows a single transaction, as a general journal entry, similar to hledger's print command and journal format (hledger_journal(5)).

The transaction's date(s) and any cleared flag, transaction code, description, comments, along with all of its account postings are shown. Simple transactions have two postings, but there can be more (or in certain cases, fewer).

UP and DOWN will step through all transactions listed in the previous account register screen. In the title bar, the numbers in parentheses show your position within that account register. They will vary depending on which account register you came from (remember most transactions appear in multiple account registers). The #N number preceding them is the transaction's position within the complete unfiltered journal, which is a more stable id (at least until the next reload).

On this screen (and the register screen), the E key will open your text editor with the cursor positioned at the current transaction if possible.

This screen has a limitation with showing file updates: it will not show them until you exit and re-enter it. So eg to see the effect of using the E key, currently you must: - press E, edit and save the file, then exit the editor, returning to hledger-ui - press g to reload the file (or use -w/--watch mode) - press LEFT then RIGHT to exit and re-enter the transaction screen.

Error

This screen will appear if there is a problem, such as a parse error, when you press g to reload. Once you have fixed the problem, press g again to reload and resume normal operation. (Or, you can press escape to cancel the reload attempt.)

TIPS

Watch mode

One of hledger-ui's best features is the auto-reloading -w/--watch mode. With this flag, it will update the display automatically whenever changes are saved to the data files.

This is very useful when reconciling. A good workflow is to have your bank's online register open in a browser window, for reference; the journal file open in an editor window; and hledger-ui in watch mode in a terminal window, eg:

$ hledger-ui --watch --register checking -C

As you mark things cleared in the editor, you can see the effect immediately without having to context switch. This leaves more mental bandwidth for your accounting. Of course you can still interact with hledger-ui when needed, eg to toggle cleared mode, or to explore the history.

There are currently some limitations with --watch:

It may not work correctly for you, depending on platform or system configuration. (Eg #836.)

At least on mac, there can be a slow build-up of CPU usage over time, until the program is restarted (or, suspending and restarting with CTRL-z fg may be enough).

It will not detect file changes made by certain editors, such as Jetbrains IDEs or gedit, or on certain less common filesystems. (To work around, press g to reload manually, or try #1617's fs.inotify.max_user_watches workaround and let us know.)

If you are viewing files mounted from another machine, the system clocks on both machines should be roughly in agreement.

Debug output

You can add --debug[=N] to the command line to log debug output. This will be logged to the file hledger-ui.log in the current directory. N ranges from 1 (least output, the default) to 9 (maximum output).

ENVIRONMENT

COLUMNS The screen width to use. Default: the full terminal width.

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

BUGS

We welcome bug reports in the hledger issue tracker (shortcut: http://bugs.hledger.org), or on the #hledger chat or hledger mail list (https://hledger.org/support).

Some known issues:

-f- doesn't work (hledger-ui can't read from stdin).

If you press g with large files, there could be a noticeable pause.

The Transaction screen does not update from file changes until you exit and re-endter it (see SCREENS > Transaction above).

--watch is not yet fully robust on all platforms (see Watch mode above).

hledger-web

NAME

hledger-web - robust, friendly plain text accounting (Web version)

SYNOPSIS

hledger-web [--serve|--serve-api] [OPTS] [ARGS]
hledger web -- [--serve|--serve-api] [OPTS] [ARGS]

DESCRIPTION

This manual is for hledger's web interface, version 1.33.99. See also the hledger manual for common concepts and file formats.

hledger is a robust, user-friendly, cross-platform set of programs for tracking money, time, or any other commodity, using double-entry accounting and a simple, editable file format. hledger is inspired by and largely compatible with ledger(1), and largely interconvertible with beancount(1).

hledger-web is a simple web application for browsing and adding transactions. It provides a more user-friendly UI than the hledger CLI or hledger-ui TUI, showing more at once (accounts, the current account register, balance charts) and allowing history-aware data entry, interactive searching, and bookmarking.

hledger-web also lets you share a journal with multiple users, or even the public web. There is no access control, so if you need that you should put it behind a suitable web proxy. As a small protection against data loss when running an unprotected instance, it writes a numbered backup of the main journal file (only) on every edit.

Like hledger, it reads from (and appends to) a journal file specified by the LEDGER_FILE environment variable (defaulting to $HOME/.hledger.journal); or you can specify files with -f options. It can also read timeclock files, timedot files, or any CSV/SSV/TSV file with a date field. (See hledger(1) -> Input for details.)

hledger-web can be run in three modes:

  • Transient mode (the default): your default web browser will be opened to show the app if possible, and the app exits automatically after two minutes of inactivity (no requests received and no open browser windows viewing it).

  • With --serve: the app runs without stopping, and without opening a browser.

  • With --serve-api: only the JSON API is served.

In all cases hledger-web runs as a foreground process, logging requests to stdout.

OPTIONS

hledger-web provides the following options:

--serve : serve and log requests, don't browse or auto-exit after timeout

--serve-api : like --serve, but serve only the JSON web API, not the web UI

--allow=view|add|edit : set the user's access level for changing data (default: add). It also accepts sandstorm for use on that platform (reads permissions from the X-Sandstorm-Permissions request header).

--cors=ORIGIN : allow cross-origin requests from the specified origin; setting ORIGIN to "*" allows requests from any origin

--host=IPADDR : listen on this IP address (default: 127.0.0.1)

By default the server listens on IP address 127.0.0.1, which is accessible only to requests from the local machine.. You can use --host to listen on a different address configured on the machine, eg to allow access from other machines. The special address 0.0.0.0 causes it to listen on all addresses configured on the machine.

--port=PORT : listen on this TCP port (default: 5000)

Similarly, you can use --port to listen on a TCP port other than 5000. This is useful if you want to run multiple hledger-web instances on a machine.

--socket=SOCKETFILE : listen on the given unix socket instead of an IP address and port (unix only; implies --serve)

When --socket is used, hledger-web creates and communicates via a socket file instead of a TCP port. This can be more secure, respects unix file permissions, and makes certain use cases easier, such as running per-user instances behind an nginx reverse proxy. (Eg: proxy_pass http://unix:/tmp/hledger/${remote_user}.socket;.)

--base-url=URL : set the base url (default: http://IPADDR:PORT).

You can use --base-url to change the protocol, hostname, port and path that appear in hledger-web's hyperlinks. This is useful eg when integrating hledger-web within a larger website. The default is http://HOST:PORT/ using the server's configured host address and TCP port (or http://HOST if PORT is 80). Note this affects url generation but not route parsing.

--test : run hledger-web's tests and exit. hspec test runner args may follow a --, eg: hledger-web --test -- --help

hledger-web also supports many of hledger's general options. Query options and arguments may be used to set an initial filter, which although not shown in the UI, will restrict the data shown, in addition to any search query entered in the UI.

Note that hledger-web shows accounts with zero balances by default, like hledger-ui (and unlike hledger). Using the -E/--empty flag at startup will hide them.

If you see accounts which appear to have a zero balance, but cannot be hidden with -E: these have a mixed-cost balance which looks like zero when costs are hidden. Currently hledger-web does not show costs at all.

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.

PERMISSIONS

By default, hledger-web allows anyone who can reach it to view the journal and to add new transactions, but not to change existing data.

You can restrict who can reach it by

  • setting the IP address it listens on (see --host above). By default it listens on 127.0.0.1, accessible to all users on the local machine.
  • putting it behind an authenticating proxy, using eg apache or nginx
  • custom firewall rules

You can restrict what the users who reach it can do, by

  • using the --capabilities=CAP[,CAP..] flag when you start it, enabling one or more of the following capabilities. The default value is view,add:
    • view - allows viewing the journal file and all included files
    • add - allows adding new transactions to the main journal file
    • manage - allows editing, uploading or downloading the main or included files
  • using the --capabilities-header=HTTPHEADER flag to specify a HTTP header from which it will read capabilities to enable. hledger-web on Sandstorm uses the X-Sandstorm-Permissions header to integrate with Sandstorm's permissions. This is disabled by default.

EDITING, UPLOADING, DOWNLOADING

If you enable the manage capability mentioned above, you'll see a new "spanner" button to the right of the search form. Clicking this will let you edit, upload, or download the journal file or any files it includes.

Note, unlike any other hledger command, in this mode you (or any visitor) can alter or wipe the data files.

Normally whenever a file is changed in this way, hledger-web saves a numbered backup (assuming file permissions allow it, the disk is not full, etc.) hledger-web is not aware of version control systems, currently; if you use one, you'll have to arrange to commit the changes yourself (eg with a cron job or a file watcher like entr).

Changes which would leave the journal file(s) unparseable or non-valid (eg with failing balance assertions) are prevented. (Probably. This needs re-testing.)

RELOADING

hledger-web detects changes made to the files by other means (eg if you edit it directly, outside of hledger-web), and it will show the new data when you reload the page or navigate to a new page. If a change makes a file unparseable, hledger-web will display an error message until the file has been fixed.

(Note: if you are viewing files mounted from another machine, make sure that both machine clocks are roughly in step.)

JSON API

In addition to the web UI, hledger-web also serves a JSON API that can be used to get data or add new transactions. If you want the JSON API only, you can use the --serve-api flag. Eg:

$ hledger-web -f examples/sample.journal --serve-api
...

You can get JSON data from these routes:

/version
/accountnames
/transactions
/prices
/commodities
/accounts
/accounttransactions/ACCOUNTNAME

Eg, all account names in the journal (similar to the accounts command). (hledger-web's JSON does not include newlines, here we use python to prettify it):

$ curl -s http://127.0.0.1:5000/accountnames | python -m json.tool
[
    "assets",
    "assets:bank",
    "assets:bank:checking",
    "assets:bank:saving",
    "assets:cash",
    "expenses",
    "expenses:food",
    "expenses:supplies",
    "income",
    "income:gifts",
    "income:salary",
    "liabilities",
    "liabilities:debts"
]

Or all transactions:

$ curl -s http://127.0.0.1:5000/transactions | python -m json.tool
[
    {
        "tcode": "",
        "tcomment": "",
        "tdate": "2008-01-01",
        "tdate2": null,
        "tdescription": "income",
        "tindex": 1,
        "tpostings": [
            {
                "paccount": "assets:bank:checking",
                "pamount": [
                    {
                        "acommodity": "$",
                        "aismultiplier": false,
                        "aprice": null,
...

Most of the JSON corresponds to hledger's data types; for details of what the fields mean, see the Hledger.Data.Json haddock docs and click on the various data types, eg Transaction. And for a higher level understanding, see the journal docs.

In some cases there is outer JSON corresponding to a "Report" type. To understand that, go to the Hledger.Web.Handler.MiscR haddock and look at the source for the appropriate handler to see what it returns. Eg for /accounttransactions it's getAccounttransactionsR, returning a "accountTransactionsReport ...". Looking up the haddock for that we can see that /accounttransactions returns an AccountTransactionsReport, which consists of a report title and a list of AccountTransactionsReportItem (etc).

You can add a new transaction to the journal with a PUT request to /add, if hledger-web was started with the add capability (enabled by default). The payload must be the full, exact JSON representation of a hledger transaction (partial data won't do). You can get sample JSON from hledger-web's /transactions or /accounttransactions, or you can export it with hledger-lib, eg like so:

.../hledger$ stack ghci hledger-lib
>>> writeJsonFile "txn.json" (head $ jtxns samplejournal)
>>> :q

Here's how it looks as of hledger-1.17 (remember, this JSON corresponds to hledger's Transaction and related data types):

{
    "tcomment": "",
    "tpostings": [
        {
            "pbalanceassertion": null,
            "pstatus": "Unmarked",
            "pamount": [
                {
                    "aprice": null,
                    "acommodity": "$",
                    "aquantity": {
                        "floatingPoint": 1,
                        "decimalPlaces": 10,
                        "decimalMantissa": 10000000000
                    },
                    "aismultiplier": false,
                    "astyle": {
                        "ascommodityside": "L",
                        "asdigitgroups": null,
                        "ascommodityspaced": false,
                        "asprecision": 2,
                        "asdecimalpoint": "."
                    }
                }
            ],
            "ptransaction_": "1",
            "paccount": "assets:bank:checking",
            "pdate": null,
            "ptype": "RegularPosting",
            "pcomment": "",
            "pdate2": null,
            "ptags": [],
            "poriginal": null
        },
        {
            "pbalanceassertion": null,
            "pstatus": "Unmarked",
            "pamount": [
                {
                    "aprice": null,
                    "acommodity": "$",
                    "aquantity": {
                        "floatingPoint": -1,
                        "decimalPlaces": 10,
                        "decimalMantissa": -10000000000
                    },
                    "aismultiplier": false,
                    "astyle": {
                        "ascommodityside": "L",
                        "asdigitgroups": null,
                        "ascommodityspaced": false,
                        "asprecision": 2,
                        "asdecimalpoint": "."
                    }
                }
            ],
            "ptransaction_": "1",
            "paccount": "income:salary",
            "pdate": null,
            "ptype": "RegularPosting",
            "pcomment": "",
            "pdate2": null,
            "ptags": [],
            "poriginal": null
        }
    ],
    "ttags": [],
    "tsourcepos": {
        "tag": "JournalSourcePos",
        "contents": [
            "",
            [
                1,
                1
            ]
        ]
    },
    "tdate": "2008-01-01",
    "tcode": "",
    "tindex": 1,
    "tprecedingcomment": "",
    "tdate2": null,
    "tdescription": "income",
    "tstatus": "Unmarked"
}

And here's how to test adding it with curl. This should add a new entry to your journal:

$ curl http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @txn.json

DEBUG OUTPUT

Debug output

You can add --debug[=N] to the command line to log debug output. N ranges from 1 (least output, the default) to 9 (maximum output). Typically you would start with 1 and increase until you are seeing enough. Debug output goes to stderr, interleaved with the requests logged on stdout. To capture debug output in a log file instead, you can usually redirect stderr, eg:
hledger-web --debug=3 2>hledger-web.log.

ENVIRONMENT

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

BUGS

We welcome bug reports in the hledger issue tracker (shortcut: http://bugs.hledger.org), or on the #hledger chat or hledger mail list (https://hledger.org/support).

Some known issues:

Does not work well on small screens, or in text-mode browsers.

hledger-web tips

Edit, upload, download

hledger-web does allow you to edit, upload or download the whole journal file, but for some time it has been undocumented. Use the --capabilities flag to add "manage" permission:

$ hledger-web --capabilities=view,add,manage

Then you'll see a new spanner button to the right of the search bar. Click this to edit, upload or download your main data file or included files.

The url for the editform looks like this:

http://127.0.0.1:5000/edit/%2Fabsolute%2Fpath%2Fno%2Fsymlinks%2Fto%2Fdesired.file

See also: http://hledger.org/hledger-web.html#permissions

Achievements

Some project achievements to unlock. Being a software maintainer means unending toil in mostly-obscurity, so you've got to seek out the fun where you can!

  • 1.0 release
  • packaged in major distros, binaries available
  • pretty good reference docs
  • 100 committers
  • 1k github stars
  • among top 50 starred haskell projects on github
  • discussed on Hacker News
  • match ledger IRC channel size
  • mentioned in "what good Haskell software exists" discussions
  • among top 40 starred haskell projects on github
  • multiple people providing support
  • pretty good tutorial docs
  • pretty good cookbook docs
  • 100 IRC chatters
  • 2k github stars
  • in top 30 github starred haskell projects
  • match ledger speed
  • match ledger committers
  • 200 committers
  • best in class for investment tracking
  • 2.0 release
  • in top 20 github starred haskell projects
  • match ledger stars
  • match beancount mail list
  • match ledger mail list
  • in top 10 github starred haskell projects

Benchmarks

Benchmarks are standard performance measurements, which we define using bench declarations in cabal files. There is one in hledger.cabal, with related code and data files in hledger/bench/.

To run the standard hledger benchmark, use stack bench hledger. This installs haskell dependencies (but not system dependencies) and rebuilds as needed, then runs hledger/bench/bench.hs, which by default shows quick elapsed-time measurements for several operations on a standard data file:

$ stack bench hledger
NOTE: the bench command is functionally equivalent to 'build --bench'
...
hledger-0.27: benchmarks
Running 1 benchmarks...
Benchmark bench: RUNNING...
Benchmarking hledger in /Users/simon/src/hledger/hledger with timeit
read bench/10000x1000x10.journal        [1.57s]
print                                   [1.29s]
register                                [1.92s]
balance                                 [0.21s]
stats                                   [0.23s]
Total: 5.22s
Benchmark bench: FINISH

bench.hs has some other modes, which you can use by compiling and running it directly. --criterion reports more detailed and dependable measurements, but takes longer:

$ cd hledger; stack exec -- ghc -ibench bench/bench && bench/bench --criterion
...
Linking bench/bench ...
Benchmarking hledger in /Users/simon/src/hledger/hledger with criterion
benchmarking read bench/10000x1000x10.journal
time                 1.414 s    (1.234 s .. 1.674 s)
                     0.996 R²   (0.989 R² .. 1.000 R²)
mean                 1.461 s    (1.422 s .. 1.497 s)
std dev              59.69 ms   (0.0 s .. 62.16 ms)
variance introduced by outliers: 19% (moderately inflated)

benchmarking print
time                 1.323 s    (1.279 s .. 1.385 s)
                     1.000 R²   (0.999 R² .. 1.000 R²)
mean                 1.305 s    (1.285 s .. 1.316 s)
std dev              17.20 ms   (0.0 s .. 19.14 ms)
variance introduced by outliers: 19% (moderately inflated)

benchmarking register
time                 1.995 s    (1.883 s .. 2.146 s)
                     0.999 R²   (0.998 R² .. NaN R²)
mean                 1.978 s    (1.951 s .. 1.995 s)
std dev              25.09 ms   (0.0 s .. 28.26 ms)
variance introduced by outliers: 19% (moderately inflated)

benchmarking balance
time                 251.3 ms   (237.6 ms .. 272.4 ms)
                     0.998 R²   (0.997 R² .. 1.000 R²)
mean                 260.4 ms   (254.3 ms .. 266.5 ms)
std dev              7.609 ms   (3.192 ms .. 9.638 ms)
variance introduced by outliers: 16% (moderately inflated)

benchmarking stats
time                 325.5 ms   (299.1 ms .. 347.2 ms)
                     0.997 R²   (0.985 R² .. 1.000 R²)
mean                 329.2 ms   (321.5 ms .. 339.6 ms)
std dev              11.08 ms   (2.646 ms .. 14.82 ms)
variance introduced by outliers: 16% (moderately inflated)

--simplebench shows a table of elapsed-time measurements for the commands defined in bench/default.bench. It can also show the results for multiple h/ledger executables side by side, if you tweak the bench.hs code. Unlike the other modes, it does not link with the hledger code directly, but runs the "hledger" executable found in $PATH (so ensure that's the one you intend to test).

$ cd hledger; stack exec -- ghc -ibench bench/bench && bench/bench --simplebench
Benchmarking /Users/simon/.local/bin/hledger in /Users/simon/src/hledger/hledger with simplebench and shell
Using bench/default.bench
Running 4 tests 1 times with 1 executables at 2015-08-23 16:58:59.128112 UTC:
1: hledger -f bench/10000x1000x10.journal print	[3.27s]
1: hledger -f bench/10000x1000x10.journal register	[3.65s]
1: hledger -f bench/10000x1000x10.journal balance	[2.06s]
1: hledger -f bench/10000x1000x10.journal stats	[2.13s]

Summary (best iteration):

+-----------------------------------------++---------+
|                                         || hledger |
+=========================================++=========+
| -f bench/10000x1000x10.journal print    ||    3.27 |
| -f bench/10000x1000x10.journal register ||    3.65 |
| -f bench/10000x1000x10.journal balance  ||    2.06 |
| -f bench/10000x1000x10.journal stats    ||    2.13 |
+-----------------------------------------++---------+

bench's --simplebench mode is based on a standalone tool, tools/simplebench.hs. simplebench.hs is a generic benchmarker of one or more executables (specified on the command line) against one or more sets of command-line arguments (specified in a file). It has a better command-line interface than bench.hs, so you may find it more convenient for comparing multiple hledger versions, or hledger and ledger. Eg:

$ stack exec -- ghc tools/simplebench
[1 of 1] Compiling Main             ( tools/simplebench.hs, tools/simplebench.o )
Linking tools/simplebench ...
$ tools/simplebench -h
tools/simplebench -h
simplebench: at least one executable needed
bench [-f testsfile] [-n iterations] [-p precision] executable1 [executable2 ...]

Run some functional tests with each of the specified executables,
where a test is "zero or more arguments supported by all executables",
and report the best execution times.

  -f testsfile   --testsfile=testsfile    file containing tests, one per line, default: bench.tests
  -n iterations  --iterations=iterations  number of test iterations to run, default: 2
  -p precision   --precision=precision    show times with this precision, default: 2
  -v             --verbose                show intermediate results
  -h             --help                   show this help

Tips:
- executables may have arguments if enclosed in quotes
- tests can be commented out with #
- results are saved in benchresults.{html,txt}
cd hledger; $ ../tools/simplebench -f bench/default.bench hledger ledger
Using bench/default.bench
Running 4 tests 2 times with 2 executables at 2015-08-24 04:24:37.257068 UTC:

Summary (best iteration):

+-----------------------------------------++---------+--------+
|                                         || hledger | ledger |
+=========================================++=========+========+
| -f bench/10000x1000x10.journal print    ||    3.24 |   0.43 |
| -f bench/10000x1000x10.journal register ||    3.80 |   3.48 |
| -f bench/10000x1000x10.journal balance  ||    2.05 |   0.18 |
| -f bench/10000x1000x10.journal stats    ||    2.10 |   0.19 |
+-----------------------------------------++---------+--------+

Finally, for quick, fine-grained performance measurements when troubleshooting or optimising, I use dev.hs.

Changelogs

See also RELEASING.md.

Update master changelogs often

Before release, and preferably daily/weekly as well:

  • during normal development, do this only in master branch
  • during release preparation.. unclear; keep the other branch's changelog in mind, they need to be synced at some point. Prioritise [minor] release branch's changelog ?
  • ./Shake changelogs
  • edit the new changelog items
    • open the five changelogs
      • in emacs: M-x dired-find, -name CHANGES.md, (, split into 5 columns, open each one, narrow from last release heading to top
    • in each changelog
      • paste section headings from top comment if needed
      • process new items, from top-most section heading upward
        • drop boring items (changes not visible to end/API users, followups to other new items, minor doc updates..)
        • drop duplicated items/content (mostly between cli and lib)
        • move to top of appropriate changelog & section (create section when needed; consider a second pane to reduce scrolling)
        • drop prefixes, edit texts, check/adjust links
        • (in cli/web/ui, move any "API changes" parts to lib ?)
    • proof read/clean up all changelogs (Obsidian works well)
    • ./Shake changelogs -c

Update release & master changelogs at release time

Before release, after cherry-picking changes from master to the release branch:

  • check out master in a separate working copy
  • for each changelog
    • open master & release changelog in side-by-side windows
    • copy all appropriate new content from master to release
    • in release changelog, remove any previous prerelease heading, add a new release heading
    • in release ui/web changelogs, add/update "uses hledger X.Y" item if needed
    • if this is a major release, or a minor release with notable project updates, copy to master changelogs:
      • the ui/web "uses hledger X.Y"
      • the new release heading
  • commit changelog updates in both working copies
  • pull the master changelog updates back to main working copy
  • destroy temporary working copy, emacs buffers

Update release notes

  • add a new section in site/src/release-notes.md
  • copy/rewrite/summarise package changelogs
  • note any other items of interest
  • list release contributors
  • write release summary

Old notes

Changelogs started using markdown from 1.0 onward. Should make consistent.

Changelogs started mentioning committer names from 2017/1, for hledger-ui-1.1.1 (because they won't appear on the release notes). Could do it just for minor releases but might as well do it for all. Could do it for past releases but no pressing need.

In site/release-notes.md, we stopped mentioning minor releases around 0.27. The old minor releases should probably be removed or promoted to the same heading level as major releases.

Code

hledger is a suite of applications, tools and libraries. The main hledger code repository is github.com/simonmichael/hledger (shortcut url code.hledger.org). There are also various hledger addons maintained as separate projects with their own repos.

hledger packages

Within the main repo, there are a number of separate cabal packages, making it easier to pick and choose parts of hledger to install or to package. They are:

hledger-lib

package, code

Core data models, parsing, standard reports, and utilities. Most data types are defined in Hledger.Data.Types, while functions that operate on them are defined in Hledger.Data.TYPENAME. Under Hledger.Read are parsers for the supported input formats. Data files are parsed into a Journal, which contains a list of Transactions, each containing multiple Postings of some MixedAmount (multiple single-CommoditySymbol Amounts) to some AccountName. When needed, the Journal is further processed to derive a Ledger, which contains summed Accounts. In Hledger.Reports there are standard reports, which extract useful data from the Journal or Ledger.

Here's a diagram of the main data model:

diagram

hledger

package, code, manual

hledger's command line interface, and command line options and utilities for other hledger tools.

Try tracing the execution of a hledger command:

  1. Hledger.Cli.Main:main parses the command line to select a command, then
  2. gives it to Hledger.Cli.Utils:withJournalDo, which runs it after doing all the initial parsing.
  3. Parsing code is under hledger-lib:Hledger.Read, eg Hledger.Read.JournalReader.
  4. Commands extract useful information from the parsed data model using hledger-lib:Hledger.Reports, and
  5. render in plain text for console output (or another output format, like CSV).
  6. Everything uses the data types and utilities from hledger-lib:Hledger.Data and hledger-lib:Hledger.Utils.

hledger-ui

package, code, manual

A terminal interface.

hledger-web

package, code, manual

A web interface. hledger-web starts a web server built with the yesod framework, and (by default) opens a web browser view on it. It reads the journal file(s) at startup and again whenever they change. It can also write (append) new transactions to the journal file.

There are two main views, which can be filtered with queries:

  • /journal, showing general journal entries (like hledger print)

  • /register, showing transactions affecting an account (slightly different from hledger's register command, which shows postings).

There is also:

  • a sidebar (toggled by pressing s) showing the chart of accounts (like hledger balance)
  • an add form for adding new transactions (press a)
  • a help dialog showing quick help and keybindings (press h or click ?)

Most of the action is in

Handler module and function names end with R, like the yesod-generated route type they deal with.

Dynamically generated page content is mostly inline hamlet. Lucius/Julius files and widgets generally are not used, except for the default layout.

Here are some ways to run it during development:

  • yesod devel: runs in developer mode, rebuilds automatically when config, template, static or haskell files change (but only files in the hledger-web package):
$ (cd hledger-web; yesod devel)
  • yesod-fast-devel may be a good alternative, also reloads the browser page

  • stack ghci: runs the server in developer mode from GHCI. Changes to static files like hledger.js will be visible on page reload; to see other changes, restart it as shown.

$ (cd hledger-web; stack ghci hledger-web)
hledger-web> :main --serve   # restart: ctrl-c, :r, enter, ctrl-p, ctrl-p, enter
  • just ghci-web: runs the server in developer mode from GHCI, also interprets the hledger-lib and hledger packages so that :reload picks up changes in those packages too:
$ just ghci-web
ghci> :main --serve

(This rule also creates symbolic links to hledger-web's config, messages, static and templates directories, needed in developer mode, so it can run from the top directory. This may not work on Windows.)

Quality

Relevant tools include:

  • unit tests
  • functional tests
  • performance tests
  • documentation tests
  • ui tests (manual)
  • installation tests
  • code reviews

Code review

COMMITS

In the hledger project we try to follow certain conventions for commit messages, because good messages lead to good commits => good change docs => easier code review => quicker merging => faster delivery of quality software. We'll check and help you polish messages as part of CI and code review. (You can also set up a local commit hook, described below.)

Here's the typical format:

[feat|imp|fix[!]:] topic: Summary

[Longer description when useful]

More precisely:

  • Commit messages must begin with one or more prefixes (colon-terminated words), indicating the type and/or topic.
  • Commits causing user-visible changes must begin with feat:, imp: or fix: (feature, improvement, or bugfix). These will be used in release notes. If they are breaking/incompatible changes, use feat!:, imp!: or fix!:.
  • To skip CI builds on commits which would normally trigger one, add a ; at the beginning. (Our CI does a lot of work, so you can use this to reduce energy waste and carbon emissions from minor changes. Non-code commits do this automatically.)
  • Mention any relevant issue numbers, usually parenthesised at the end. (#NNNN)
  • Try to write commit messages as changelog/release-note-ready documentation that will tell their intended audience (which might be users, installers, packagers, and/or developers) what they need to know.

Some examples:

  • feat: accounts: --types shows account types (#1820)
  • imp!: journal: Remove deprecated account type code syntax from account directives.
  • fix: types: Ensure auto postings can match against and be matched by type: queries.
  • tools: commitlint: allow a git "fixup! " prefix
  • doc: releasing: tweaks

Some possible prefixes:

  • feat - a new feature
  • imp - an improvement to existing features
  • fix - a bugfix
  • dev - a generic developer change
  • ref - refactoring
  • cln - cleanup
  • doc - documentation-related
  • test - tests-related
  • ci - continuous integration-related
  • Any of the standard labels used in the issue tracker.

How to check commits

Before committing, pushing, or merging, run tools/commitlint to check recent commit messages. (See the script for more ways to select commits.) You can configure your local working copy to do this automatically, by running just installcommithook.

commitlint also runs automatically on Github to check pull requests.

See also

Contributor Quick Start

New contributors are always welcome in the hledger project. Jump in! Browse the ideas below, or say hello in the chat and we'll help find you a job.

First steps

Ideas for contributing as a ...

Visitor

  • Give feedback on the site and your impressions of the project

New user

  • Give feedback on your new user experience

Developer

Developer using the hledger libraries

  • Give feedback on your experience using the hledger packages
  • Suggest API improvements

Packager

  • Improve hledger's packaging on one or more platforms

Communicator

Marketing and market understanding is vital.

  • clarify project goals, value proposition, brand, mission, story
  • monitor product-market fit
  • identify new opportunities
  • influence developer priorities
  • spread the word!

Funder

Become a financial backer to sustain and grow this project, increase your influence, express gratitude, build prosperity consciousness, and help transform world finance!

  • Use the donate links on the home page
  • Configure a recurring donation
  • Contribute or pledge bounties on issues you care about
  • Ask your organization to contribute
  • Work on project sustainability, accountability, fundraising

Tester

  • Test installation on platforms you have access to
  • Test examples, advice, and links in the docs
  • Run the latest release or developer build in daily use
  • Run tests
  • Run benchmarks
  • Report packaging, documentation, UX, functional, performance issues
  • Report and help analyse problems via irc/mail list/bug tracker

When reporting bugs, don't forget to search the tracker for a similar bug report. Otherwise, open a new bug by clicking "New issue", or http://bugs.hledger.org/new.

Enhancement requests are sometimes added to the tracker,but for these consider using the IRC channel and mail list (see Getting help). Both are archived and linkable, so the idea won't be lost. There is also a collection of wishes at the old trello board.

Bug wrangler

Tech support provider

Technical Writer

  • get familiar with the website and documentation online, review and test
  • get familiar with the site/doc source files (see Shake.hs)
  • get the latest hledger source
  • send patches with names prefixed with "doc: " (or "site: ")

Graphics Designer

  • more/better logos & graphics
  • illustrations and diagrams
  • web design mockups for home page, site, hledger-web UI

Maintainer

Help with issue management

  • watch tracker activity, report status
  • apply/update labels where needed
  • follow up on dormant issues
  • facilitate a consistently good bug-reporting & PR-contributing experience

Help with packaging

  • package hledger for linux distros, macports, etc.
  • develop mac/windows installers
  • find and assist distro packagers/installer developers

Help with project management

  • clarify/update goals and principles
  • monitor, report on project progress and performance
  • research, compare and report on successful projects, related projects
  • identify collaboration opportunities
  • marketing, communication, outreach
  • release management, roadmap planning

CREDITS

hledger is brought to you by the

  • Issue wranglers,
  • Bug hunters,
  • Design dreamers,
  • Code slingers,
  • Doc poets,
  • Package marshals,
  • Helping hands,
  • Good news preachers,
  • Bank rollers,
  • Broom pushers,

by the pioneering John Wiegley, who opened up this territory with Ledger,

and by the innumerable other benefactors making it all possible.

Commit authors

https://github.com/simonmichael/hledger/graphs/contributors

11231 commits in 16 years by 155 people as of 2022-12-21:

CommitsAuthorNotes (chat me with updates!)
9749Simon Michaelfounder, project leader, lead developer
460Stephen Morganperformance, code cleanup, runtime error removal, cli/ui output, periodic transactions, parsing, deps, valuation, --gain report, hlint, lensification
161Dmitry Astapovroi, files commands; --transpose; merge/improve --budget; generalise --forecast/--auto; docker packaging; improved CSV parsing, balancing, periodic transactions, close, parsing, docs, tests
81Vladimir Zhelezovnew bash shell completions
72Alex Chenparsing improvements, code cleanups, better error messages; dep updates
52Mykola Orliukhledger-budget, hledger-prices addons; scientific number notation; print, hledger-equity, hledger-rewrite, --pivot, space, parsing improvements; code updates; GHC 8.0 support
51Jakob Schöttlbash completions; register --invert; timeclock parsing improvements; code cleanups
40Everett Hildenbrandtdoc toolchain updates, switch from hakyll to pandoc; csv parser improvement
31Jakub Zárybnickýhledger-web, hledger-ui improvements
29Marko Kocićbuild, hlint fixes; hledger-web improvement
26Justin Lebs/cf/is improvements
26Dominik Süßhledger-web layout improvements
24Thomas R. Kollhledger-web improvements
17Peter Simonsbuild, dep fixes
17Joseph Westonparsing improvements; test, dep updates
16Aleksandar DimitrovCSV separator rule
15Nolan DarilekSandstorm app
14Brian Wignalloutput dates in YYYY-MM-DD format
12Tim Dockerparsing improvements; proper date support; P directive (first code contributor, 2008-11
12Lawrence--commodity-column; multi-day-of-week period expressions
11Trygve Laugstølhledger-web improvements; --format; CSV --rules-file, stdin support, separate in/out fields, interpolated description; test updates
11Jesse Rosenthalparsing improvementss
10Nick Ingoliainclude directive, account directive; parsing improvements (second code contributor, 2008-12
10Caleb Maclennanpayees/notes/descriptions commands; print improvement; doc, code, test updates
10Julien Moutinhoprint CSV output; hledger-equity, hledger-rewrite, hledger-web, parsing improvements; hledger-check-dates addon
10Henning Thielemannhledger-web improvements, quarter periods, code cleanup
9Ryan Desfosseswarnings, docs, hledger-web fixes
9Eli Flanaganhledger-web date picker; build, doc updates
8Imulisupport multiple input files
8Felix Yandep updates; doc updates
8Nicholas Nirobalancesheetequity command; balance tests, fix
8Moritz Kiefer--pretty-tables; dep updates; switch to megaparsec; cabal.project file; space leak fixes
7Jacob WeiszSandstorm app improvements
7Hans-Peter Deifeldep updates; csv rules parsing fix; command line parsing improvements
6Clint AdamsCSV account2 setting; dep updates; cabal test suites
6Roman Cheplyakachart command; hledger add improvements; --no-new-accounts
6Gaith Hallakundo (<) in hledger add; command line parsing improvement
6Samuel Maymulticommodity balance assertions
5Martin Michlmayrdoc updates
5Jacek Generowiczcommand line parsing fix
5Eric Mertens--pretty-tables improvements; CSV whitespace fix
5Xinruo Sunhledger-web --static-root; hledger-web autocomplete improvements; hledger print tag filtering
4Damien Cassoupayee directive, check improvements, info manual directory entries
4Michael Snoymanhledger-web improvements
4Sergey Astaninunicode support
3Michael Sanders& (AND) operator in CSV if rules
3Christian G. Wardencashflow tweaks; hledger-rewrite (auto postings) commodity substitution
3Eric Kowhledger add improvements
3toonnPR template improvements
3Carlos Lopez-Cameyhledger-web add form improvements
3Johannes Gererbalance assignments; generalise parser types
3Malte Brandy--pivot, newline in CSV rule-generated comments
2Pavlo Keresteyquoting fixes
2Alejandro García Montorohledger-web --cors
2Gergely Riskocomment directive
2aragaerfix commodity checking fix; fix --drop with csv
2Stefano Rodighierohledger-dupes addon
2Arsen Arsenovićhledger-web XSS fix
2crocketimprove hledger-ui editor support
2Arnout Engelenhledger-web register chart improvements
2gwernwhitespace, Haskell98 cleanups
2Arjen Langebaerd-c/--commodity-style option
2jungle-boogietutorial updates
2Max Bolingbrokeunicode-aware regexes; csv date-format rule
2Matthias Kauerinvestment doc improvements
2Elijah Cainegit/nix tweaks
2Judah Jacobsonreadline editing, tab completion in hledger add
2Ben Creasydoc updates
2Christoph Nicolaidoc updates
2Sergei Trofimovichbounds, build updates
2Sam Jeevesbalance assertion line number reporting
2Alex Hirzeldoc updates
2Pranesh Prakashdoc updates
1flip111
1jeevcat
1legrostdg
1max thomas
1trevorriles
1zieone
1Aerex
1ˌbodʲɪˈɡrʲim
1Aiken Cairncross
1Alan Young
1Alvaro Fernando García
1Amarandus
1Amitai Burstein
1Andreas Pauley
1Andrew Jones
1Andriy Mykhaylyk
1Ben Boeckel
1Boyd Kelly
1Brian Scott
1Bryan Richter
1Carel Fellinger
1Carl Richard Theodor Schneider
1Charlotte Van Petegem
1Colin Woodbury
1Daniel Gröber
1David D Lowe
1David Reaver
1David Zhang
1Doug Goldstein
1Evilham
1Felix Van der Jeugt
1Fun Ilrys (Nissar Chababy)
1Gabriel Ebner
1Garret McGraw
1Jan Zerebecki
1Jeff Richards
1Joachim Breitner
1Joaquin "Florius" Azcarate
1Joe Horsnell
1Johann Klähn
1John Wiegley
1Joshua Chia
1Joshua Kehn
1Kyle Marek-Spartz
1Lawrence Wu
1Luca Molteni
1Léo Gaspard
1M Parker
1Mark Hansen
1Mateus Furquim
1Michael Kainer
1Michael Walker
1Mick Dekkers
1Mitchell Rosen
1Nadrieril
1Nicolas Wavrant
1Nikhil Jha
1Nissar Chababy
1Oliver Braun
1Omari Norman
1Patrick Fiaux
1Patrik Keller
1Pavan Rikhi
1Pia Mancini
1Rick Lupton
1Rui Chen
1Sam Doshi
1ShrykeWindgrace
1Shubham Lagwankar
1Simon Hengel
1SpicyCat
1Steven R. Baker
1TANIGUCHI Kohei
1Timofey ZAKREVSKIY
1Vladimir Sorokin
1Wad
1afarrow
1agander
1awjchen
1azure-pipelines[bot]
1charukiewicz

Decisions

A partial list of notable development decisions.

2022

Replace "transaction price" terminology with "cost"

"Transaction price" never quite stuck. "Cost" is simpler, shorter, more intuitive, consistent with --cost and "cost reporting", and more distinct from "market price".

There is an (acceptable) ambiguity: "cost" could mean the @ UNITCOST price attached to the amount, or the total cost when the amount is converted (QUANTITY * UNITCOST).

Status: as of 2023Q1 this has been done in the manuals and is slowly ongoing in the code.

2023

Plugin types

We will document and support where feasible several distinct kinds of plugin, written in haskell or other languages, such as reader, processor, writer, formatter, command. See https://hledger.org/scripting.html#plugin-types.

Developer workflows

Get developer tools

Ensure stack is installed (or if you’re a cabal expert, feel free to use that.)

Ensure git is installed. On Windows, it comes with stack.

Here are some useful optional tools:

  • just for automating project tasks
  • GNU Make for automating tasks in a few subdirectories
  • watchexec for re-running commands when files change
  • ghcid gives real-time GHC feedback as you make code changes.
  • shelltestrunner runs hledger's functional tests.
  • quickbench measures and reports time taken by commands.
  • hasktags generates tag files for quick code navigation in editors like Emacs and vim.
  • For browsing and editing Haskell code, popular tools include: Emacs, Vim, IDEA, VS Code, Atom..

Eg:

stack install ghcid shelltestrunner hasktags
git clone https://github.com/simonmichael/quickbench; cd quickbench; stack install  # must run in source dir

Get the code

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

Review code

Build in place

See also https://hledger.org/install.html#c.-build-the-development-version .

stack build    # hledger hledger-ui ...

This fetches the required GHC version and haskell dependencies from the default stackage snapshot (configured in stack.yaml), then builds all hledger packages. This can take a while! To save time, you can build individual packages, eg just the CLI and TUI.

Note stack does not fetch C libraries such as curses or terminfo, which you might need to install yourself, using your system's package manager. In case of trouble, see download.

If you want to use an older snapshot/GHC for some reason, specify one of the older stack-*.yaml files:

stack --stack-yaml stack8.2.yaml build

Run in place

stack exec -- hledger     # ARGS...
stack exec -- hledger-ui  # ARGS...
stack exec -- which hledger

Build and install

This builds and also copies the hledger executables to ~/.local/bin or the Windows equivalent (which you should add to your $PATH).

stack install    # hledger hledger-ui ...

Run package tests

Runs any HUnit/doctest/easytest tests defined by each hledger package.

stack test    # hledger ...

Run package benchmarks

Runs any performance reports defined by each hledger package.

stack bench    # hledger ...

Run quickbench benchmarks

Times the end-user commands in bench.sh using quickbench.

just bench

Run functional tests

Runs the shelltestrunner tests defined in hledger/test/, which test the hledger CLI.

just functest

Run haddock tests

Checks for anything that would break haddock doc generation.

just haddocktest

Checks for the unit-tests embedded in documentation.

just doctest

Simulate Travis tests

Locally runs tests similar to what we run on Travis CI.

just travistest

Test with all supported GHC versions/stackage snapshots

just allsnapshotstest

Use GHCI

GHCI is GHC's REPL, useful for exploring and calling code interactively.

If you try to run GHCI (or things based on it, like ghcid) right after cloning the hledger repo, you might see an error about CPP macros, eg like on #961. To fix this, build the hledger packages once, eg stack build. (Or stack build hledger might be enough, depending what you are doing.)

Get a GHCI prompt for hledger-lib:

cd hledger-lib; stack ghci hledger-lib

Changing into the package directory isn't actually needed, but it enables a custom .ghci script which sets a more useful short prompt.

Get a GHCI prompt for hledger:

cd hledger; stack ghci hledger

Get a GHCI prompt for hledger-ui:

cd hledger-ui; stack ghci hledger-ui

Get a GHCI prompt for hledger-web:

cd hledger-web; stack ghci hledger-web

hledger-web also needs to find some things in its current directory (like the static/ directory). This normally just works, if not please send details.

Add a test

  • identify what to test
  • choose the test type: unit ? functional ? benchmark ?
  • currently expected to pass or fail ?
  • figure out where it goes
  • write test, verify expected result
  • get it committed

Fix a bug or add a feature

  • research, discuss, validate the issue/feature on irc/list/bug tracker
  • look for related tests, run the tests and check they are passing
  • add a test ?
  • develop a patch
  • include any related issue numbers in the patch name, eg: "fix for blah blah (#NNN)"
  • get it committed

Get your changes accepted

Follow the usual github workflow:

  • fork the main hledger repo on github,
  • git clone it to your local machine,
  • git commit, after (?) pulling and merging the latest upstream changes
  • git push back to github,
  • open a pull request on github,
  • follow up on any discussion there.

If you're new to this process, help.github.com may be useful.

Add yourself to the contributor list

Work on docs

Most docs tasks are handled by Shake.

List Shake rules:

./Shake

Generate man/info/txt manuals (in hledger*/) and embed in hledger executables:

./Shake manuals
stack build

Generate html manuals and the hledger website (in site/_site/):

./Shake website

To remove all files generated by Shake:

./Shake Clean

Use ghcid for watching GHC/GHCI

ghcid is the most reliable and fastest way to see GHC's feedback, and optionally run tests or a GHCI command, as you edit. We run it via just, for convenience and to watch multiple packages rather than just one. Run just h ghcid to list related rules.

Watch for compile errors in hledger-lib and hledger:

just ghcid

Watch compile errors and the output of some hledger command:

ghcid -c 'just ghci' -T ':main -f a.j bal --budget -N'

Use --file-watch for watching stack

stack's --file-watch flag will re-run build/test/bench when source files or package.yaml/cabal files change. Eg:

stack test hledger --file-watch

If you find that adding --fast makes this any faster, please update this.

Use entr for watching arbitrary commands

entr is the most robust cross-platform tool for watching files and running a command when they change. Note its first argument must be an executable program, to run a shell command or multiple commands use bash -c "...".

Rerun a single functional test as you change it:

ls hledger/test/budget/budget.test | entr bash -c 'clear; COLUMNS=80 stack exec -- shelltest --execdir hledger/test/budget/budget.test -i12'

Docs

Four kinds of documentation

"There is a secret that needs to be understood in order to write good software documentation: there isn’t one thing called documentation, there are four. They are: tutorials, how-to guides, explanation and technical reference. They represent four different purposes or functions, and require four different approaches to their creation." --[Daniele Procida] (https://news.ycombinator.com/item?id=21289832)

hledger's documentation structure

2019: out of date, needs update.

Project documentation lives in a number of places:

  • site/*.md is the hledger.org website content, which is generated with hakyll[-std] and pandoc
  • haddock documentation in the code appears on Hackage
  • short blurbs: cabal files, module headers, HCAR, GSOC project, ..
  • doc/notes.org has some old developer notes
  • developer reports (profiles, benchmarks, coverage..) in doc/profs, sometimes published at hledger.org/profs
  • https://github.com/simonmichael/hledger/tree/master/doc

site/ is now a symlink to the separate hledger_site repo.

See also Shake.hs.

hledger doc files can be divided into several groups:

  1. Project admin/dev notes not published on the website. These are kept in this directory (doc/). They include:
    doc/finance/      project finances
    doc/hcar/         Haskell Community and Activities Report entries
    doc/lib.m4        common macros used in package manuals
    doc/manpage.*     misc. templates for rendering package manuals
    doc/mockups/      exploratory developer mockups   
    doc/profs/        a place for long-term profiling/performance data
    
  2. Project doc files required to be in the top directory:
    README.md         the main project readme, displayed on github
    LICENSE           the default project license
    
  3. Code/API docs in haskell source files as haddock comments:
    hledger*/**/*.hs  haddock module and function docs in most source files
    
  4. Per-package descriptions, readmes, changelogs, and reference manuals. These are in the respective package directories:
    hledger*/package.yaml    source for package metadata (description, etc.)
    hledger*/README          package readme, displayed on hackage
    hledger*/CHANGES         package changelog, displayed on hackage
    hledger*/hledger*.m4.md  package manual source file(s)
    
  5. The project website and additional docs - home page, FAQ, tutorials, how-tos, developer guide, etc. These are in the site directory:
    site/             hledger.org website content, templates, assets
    

Workflows

The manuals and website are rendered from the top directory, primarily using Shake. First, build Shake:

$ ./Shake.hs

Then render the per-package manuals from markdown-m4 source files (*.m4.md) to text, man, info, and markdown formats. This requires some unix tools such as m4:

$ ./Shake manuals

The text/man/info manuals are embedded in the hledger executable, so a rebuild of that now will pick up the latest manuals:

$ stack build hledger

The website uses the markdown manuals. This copies them into the website, edits them for web display, concatenates them to form the one-page manual, and runs pandoc to render everything as html, adding tables of contents and the site header/footer.

$ ./Shake website

201901 docs reorg (#920, WIP)

https://groups.google.com/forum/#!topic/hledger/t2nVr3zER8Q/discussion

On Oct 26, 2018, at 1:47 PM, Simon Michael [email protected] wrote:

A quick heads-up: I am feeling like stepping back from github wiki, and reorganising our docs like so:

Two repos:

  1. hledger - code and hard docs
  • code and code docs (haddock docs & doctest examples)
  • developer docs (READMEs in md or org format)
  • product manuals (hledger*/hledger*.m4.md)
  • release notes and announcements
  • HCAR entries
  1. hledger-site - website and soft docs
  • hledger.org content, resources, site infrastructure
  • user cookbook, how-tos, articles
  • links to blog posts, discussions etc.
  • other resources relating to our web presence/marketing

If you disagree, let's discuss. Some quick considerations:

  • moving docs to the wiki hasn't affected the contribution rate
  • using the wiki increases our dependence on github and makes our work less self-contained and future-proof
  • the wiki docs don't look great, aren't very flexible, & don't integrate well with our site & static docs
  • using two docs systems increases complexity
  • dev docs in the wiki are too far from the code, and compete with READMEs

PS:

  • Why not go back to just one repo for everything ? Or if two repos, why not put all docs in one of them ?

Dev docs are most discoverable and maintainable right there in the main repo, ie as READMEs. Likewise for API docs (haddocks) and the reference manuals (hledger*/hledger*m4.md). We want all of these updated in lock step with code/tooling changes.

Other ("soft") docs are needed, but these have a more relaxed process, schedule, and scope (eg bookkeeping advice). They occasionally generate a lot of noise in the commit log, and I think it's a good to keep that out of the code history. The website (home and other pages, site design, site infrastructure) generates similar commit storms and is somewhat independent of code, so it goes in the soft docs repo too.

These are my thoughts, but I have an open mind if you see a better way.

me (Simon Michael (sm) change)
10/27/18 Still plenty of time to discuss and reconsider, but see also https://github.com/simonmichael/hledger/issues/920 https://github.com/simonmichael/hledger/issues/921

I'll probably make a start on the first one (consolidating dev docs in main repo).

Developer docs

Contributor, developer, and maintainer docs. These aim to describe and communicate the structure, processes and workflows of the hledger project - "the machine that makes the machine".

These mostly are kept in doc/ in the main hledger repo, and then symlinked into the hledger_site repo for rendering on hledger.org.

Discussion: https://hledger.org/support

Github:
http://code.hledger.org, http://site.hledger.org, http://finance.hledger.org
commits, COMMITS!
http://ci.hledger.org
http://bugs.hledger.org, http://wishes.hledger.org, unknown issues, http://prs.hledger.org, http://draftprs.hledger.org, http://readyprs.hledger.org, all issues
issues with bounty tag, bountysource bounties, codemill bounties, codefund bounties
projects.hledger.org
stars.hledger.org our rank among starred haskell projects:
2024:#36
2023:#32
2022:#34
2020:#36
2018:#53
2017:#54
2016:#71

Hackage:
packages: hledger-lib, hledger, hledger-ui, hledger-web, hledger-diff, hledger-iadd, hledger-interest, hledger-irr, *hledger*
diffs: hledger-lib, hledger, hledger-ui, hledger-web
build status: hledger-lib, hledger, hledger-ui, hledger-web
reverse deps: hledger-lib, hledger, hledger-ui, hledger-web
on hackage
......
......

Stackage:
build-constraints.yaml
open hledger-related issues
packages: hledger-lib, hledger, hledger-ui, hledger-web
versions: hledger-lib, hledger, hledger-ui, hledger-web
...

Repology:
quick hledger packaging status, detailed hledger packaging status, information for hledger, all *hledger* packages

Homebrew:
hledger
our 1-year rank among homebrew installs:
2023: #1821 of 24k (top 8%)
2020: #1520 of 10k (top 15%)

Debian:
source packages: haskell-hledger-lib, bugs, haskell-hledger, bugs, haskell-hledger-ui, bugs, haskell-hledger-web, bugs
stable: hledger, bugs, hledger-ui, bugs, hledger-web, bugs
testing: hledger, bugs, hledger-ui, bugs, hledger-web, bugs
unstable: hledger, bugs, hledger-ui, bugs, hledger-web, bugs
all: *hledger*
popcon sampled install stats: haskell-hledger, hledger, hledger-ui, hledger-web

Ubuntu:
source packages: haskell-hledger-lib, bugs, haskell-hledger, bugs, haskell-hledger-ui, bugs, haskell-hledger-web, bugs
binary packages: *hledger*

Gentoo: hledger, hledger-web, *hledger*

Fedora: hledger, *hledger*, hledger (package db), Haskell SIG

Void Linux: package search->hledger

Nix: *hledger*

Sandstorm: hledger web app & reviews, issues

Reference: fosskers GHC compatibility chart

Old trello planning board: https://trello.hledger.org

hledger-web demo: https://demo.hledger.org

ERRORS

hledger produces a variety of error messages in different situations. They are intended first for humans and secondly for parsing by tools like flycheck-hledger. Here we collect examples of these errors, which are listed below for reference, and also tested as part of our test suite. This document is the README in the hledger/test/errors/ directory, and also published as Developer docs > ERRORS on hledger.org.

Each error has

  • a name (similar to hledger check names when applicable)
  • an executable script in this directory which reproduces the error (like balanced.j)
  • which usually can also be used as a data file for manual testing (hledger -f balanced.j check)
  • sometimes supplementary data files (like csvdateparse.csv.rules)
  • an autogenerated shelltestrunner test (like balanced.test)

To update this readme with the latest error messages, run make readme. To regenerate the shelltestrunner tests, run make tests. To test all of these error messages with hledger, run make test. To test these errors with flycheck-hledger, customize flycheck-hledger-* in Emacs to enable all appropriate checks, then open the data files to see how flycheck handles them. C-c ! l opens a pane for easy viewing.

Error improvement

Work is ongoing to make our error messages more consistent and more useful (#1436, #1885, #1886..). This is a big project, and your help is welcome; every bit of progress counts, and this is a fast way to help users.

  • phase 1: update flycheck to detect journal errors of current hledger release (and keep a branch updated to detect errors of latest hledger master)
  • phase 2: survey/document current journal errors & status
  • phase 3: pick a new standard format
  • phase 4: implement standard format for all
  • phase 5: implement accurate lines for all
  • phase 6: implement accurate columns for all [where possible; we currently do not save the position of every part of the transaction, so most errors do not report columns]
  • phase 7: implement useful highlighted excerpts for all [we show imperfect but useful highlighted regions]
  • phase 8: implement accurate flycheck region for all [flycheck-detected regions are imperfect but useful]
  • phase 9: do likewise for timeclock errors
  • phase 10: do likewise for timedot errors
  • phase 11: do likewise for csv errors
  • phase 12: do likewise for other command line errors
  • phase 13: decide/add error ids/explanations/web pages ? not needed
  • phase 14: support Language Server Protocol & Visual Code

Standard error format

Here is our current preferred layout for error messages. It is similar to the parse error messages we get from megaparsec (since it's easier to follow that than change it):

hledger: Error: FILE:LOCATION:
EXCERPT
EXPLANATION

Notes:

  • line 1 includes "hledger" (dropping this would require some effort), the word "Error", and the error position
  • FILE is the file path
  • LOCATION is LINE[-ENDLINE][:COLUMN[-ENDCOLUMN]]
  • EXCERPT is a short visual snippet whenever possible, with the error region highlighted, line numbers, and colour when supported. This section must be easy for flycheck to ignore. (All lines begin with a space or a digit.)
  • EXPLANATION briefly explains the problem, and suggests remedies if possible. It can be dynamic, showing context-sensitive info. (ShellCheck's summaries are static.)
  • this layout is based on megaparsec's. For comparison, rustc puts summary on line 1 and location on line 2:
    Error[ID]: SUMMARY
    at FILE:LOCATION
    EXCERPT
    [DETAILS]
    
  • try https://github.com/mesabloo/diagnose, https://hackage.haskell.org/package/errata, https://hackage.haskell.org/package/chapelure later ?

Limitations

Here are some current limitations of hledger's error messages:

  • We report only one error at a time. You have to fix (or bypass) the current error to see any others.

  • We currently don't save perfect position information when parsing, so we sometimes report only line number(s), without column number(s).

  • For the same reason, the excerpts we show in error messages are not the original data. Instead we show a synthetic rendering that is similar enough to be explanatory.

Error messages

Here is the current status as of hledger (see version below) and flycheck-hledger g1310cb518. Click error names to see an example. The table headings mean:

  • std format - the error message follows our standard error format
  • line - correct line numbers are reported
  • column - useful column numbers are reported
  • excerpt - a useful excerpt is shown, ideally with the error highlighted (✓✓)
  • flycheck - the current flycheck release (or a PR branch) recognises the error and highlights a useful region
error namestd formatlinecolumnexcerptflycheck
accounts✓✓
assertions✓✓
autobalanced-
balanced-
commodities✓✓
ordereddates✓✓
parseable✓✓
parseable-dates✓✓
parseable-regexps✓✓
payees✓✓
recentassertions✓✓
uniqueleafnames✓✓
tcclockouttime✓✓
tcorderedactions✓✓
csvamountonenonzerosemi-std
csvamountparse
csvbalanceparse
csvbalancetypeparse
csvdateformat
csvdateparse
csvdaterule
csvdecimalmarkparse
csvifblocknonempty
csviftablefieldnames✓✓
csviftablenonempty
csviftablevaluecount
csvnoinclude
csvskipvalue
csvstatusparse
csvstdinrules
csvtwofields
csvstdinrules

hledger 1.32.99-g4dbd417c9-20240326 error messages:

accounts

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./accounts.j:4:
  | 2022-01-01
4 |     (a)               1
  |      ^

Strict account checking is enabled, and
account "a" has not been declared.
Consider adding an account directive. Examples:

account a
account a    ; type:A  ; (L,E,R,X,C,V)

assertions

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./assertions.j:4:8:
  | 2022-01-01
4 |     a               0 = 1
  |                       ^^^

Balance assertion failed in a
In commodity "" at this point, excluding subaccounts, ignoring costs,
the asserted balance is:        1
but the calculated balance is:  0
(difference: 1)
To troubleshoot, check this account's running balance with assertions disabled, eg:
hledger reg -I 'a$' cur:

autobalanced

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./autobalanced.j:3-4:
3 | 2022-01-01
  |     a               1

This transaction is unbalanced.
The real postings' sum should be 0 but is: 1
Consider adjusting this entry's amounts, or adding missing postings.

balanced

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./balanced.j:5-7:
5 | 2022-01-01
  |     a             1 A
  |     b            -1 B

This multi-commodity transaction is unbalanced.
Automatic commodity conversion is not enabled.
The real postings' sum should be 0 but is: 1 A, -1 B
Consider adjusting this entry's amounts, adding missing postings,
or recording conversion price(s) with @, @@ or equity postings.

commodities

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./commodities.j:6:
  | 2022-01-01
6 |     (a)             A 1
  |                     ^^^

Strict commodity checking is enabled, and
commodity "A" has not been declared.
Consider adding a commodity directive. Examples:

commodity A1000.00
commodity 1.000,00 A

ordereddates

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./ordereddates.j:10:
7 | 2022-01-02 p
  |     (a)               1
 
10 | 2022-01-01 p
   | ^^^^^^^^^^
   |     (a)               1

Ordered dates checking is enabled, and this transaction's
date (2022-01-01) is out of order with the previous transaction.
Consider moving this entry into date order, or adjusting its date.

parseable-dates

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./parseable-dates.j:3:1:
  |
3 | 2022/1/32
  | ^^^^^^^^^

This is not a valid date, please fix it.

parseable-regexps

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./parseable-regexps.j:3:8:
  |
3 | alias /(/ = a
  |        ^

This regular expression is malformed, please correct it:
(

parseable

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./parseable.j:3:2:
  |
3 | 1
  |  ^
unexpected newline
expecting date separator or digit

payees

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./payees.j:6:
6 | 2022-01-01 p
  |            ^
  |     (a)             A 1

Strict payee checking is enabled, and
payee "p" has not been declared.
Consider adding a payee directive. Examples:

payee p

recentassertions

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./recentassertions.j:11:
   | 2022-01-09 bad1
11 |     a               0
   |     ^

The recentassertions check is enabled, so accounts with balance assertions must
have a balance assertion within 7 days of their latest posting.
In account "a", this posting is 8 days later
than the last balance assertion, which was on 2022-01-01.

Consider adding a more recent balance assertion for this account. Eg:

2024-03-26
    a     0 =  0  ; (adjust asserted amount)

uniqueleafnames

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./uniqueleafnames.j:12:
  | 2022-01-01 p
9 |     (a:c)               1
 ...
   | 2022-01-01 p
12 |     (b:c)               1
   |        ^

Checking for unique account leaf names is enabled, and
account leaf name "c" is not unique.
It appears in these account names, which are used in 2 places:
a:c
b:c

Consider changing these account names so their last parts are different.

tcclockouttime

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./tcclockouttime.timeclock:5:1:
  | i 2022-01-01 00:01:00   
5 | o 2022-01-01 00:00:00   
  |   ^^^^^^^^^^^^^^^^^^^

This clockout time (2022-01-01 00:00:00) is earlier than the previous clockin.
Please adjust it to be later than 2022-01-01 00:01:00.

tcorderedactions

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./tcorderedactions.timeclock:8:1:
8 | i 2022-01-01 00:01:00   
  | ^

Expected a timeclock o entry but got i.
Only one session may be clocked in at a time.
Please alternate i and o, beginning with i.

csvamountonenonzero

hledger: Error: in CSV rules:
While processing CSV record: "2022-01-03","1","2"
while calculating amount for posting 1
rule "amount-in %2" assigned value "1"
rule "amount-out %3" assigned value "2"

Multiple non-zero amounts were assigned for an amount field.
Please ensure just one non-zero amount is assigned, perhaps with an if rule.
See also: https://hledger.org/hledger.html#setting-amounts
(hledger manual -> CSV format -> Tips -> Setting amounts)

csvamountparse

hledger: Error: error: could not parse "badamount" as an amount
CSV record: "2022-01-03","badamount"
the amount rule is: %2
the date rule is: %1

the parse error is:      1:10:
  |
1 | badamount
  |          ^
unexpected end of input
expecting '+', '-', or number

you may need to change your amount*, balance*, or currency* rules, or add or change your skip rule

csvbalanceparse

hledger: Error: error: could not parse "badbalance" as balance1 amount
CSV record: "2022-01-03","badbalance"
the balance rule is: %2
the date rule is: %1

the parse error is:      1:11:
  |
1 | badbalance
  |           ^
unexpected end of input
expecting '+', '-', or number

csvbalancetypeparse

hledger: Error: balance-type "badtype" is invalid. Use =, ==, =* or ==*.
CSV record: "2022-01-01","1"
the balance rule is: %2
the date rule is: %1

csvdateformat

hledger: Error: error: could not parse "a" as a date using date format "YYYY/M/D", "YYYY-M-D" or "YYYY.M.D"
CSV record: "a","b"
the date rule is:   %1
the date-format is: unspecified
you may need to change your date rule, add a date-format rule, or change your skip rule
for m/d/y or d/m/y dates, use date-format %-m/%-d/%Y or date-format %-d/%-m/%Y

csvdateparse

hledger: Error: error: could not parse "baddate" as a date using date format "%Y-%m-%d"
CSV record: "baddate","b"
the date rule is:   %1
the date-format is: %Y-%m-%d
you may need to change your date rule, change your date-format rule, or change your skip rule
for m/d/y or d/m/y dates, use date-format %-m/%-d/%Y or date-format %-d/%-m/%Y

csvdaterule

hledger: Error: offset=0:
Please specify (at top level) the date field. Eg: date %1

csvdecimalmarkparse

hledger: Error: decimal-mark's argument should be "." or "," (not "badmark")

csvifblocknonempty

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./csvifblocknonempty.csv.rules:2:1:
  |
2 | if foo
  | ^
start of conditional block found, but no assignment rules afterward
(assignment rules in a conditional block should be indented)

csviftablefieldnames

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./csviftablefieldnames.csv.rules:2:9:
  |
2 | if,date,nosuchfield,description
  |         ^^^^^^^^^^^^
unexpected "nosuchfield,"
expecting "account1", "account10", "account11", "account12", "account13", "account14", "account15", "account16", "account17", "account18", "account19", "account2", "account20", "account21", "account22", "account23", "account24", "account25", "account26", "account27", "account28", "account29", "account3", "account30", "account31", "account32", "account33", "account34", "account35", "account36", "account37", "account38", "account39", "account4", "account40", "account41", "account42", "account43", "account44", "account45", "account46", "account47", "account48", "account49", "account5", "account50", "account51", "account52", "account53", "account54", "account55", "account56", "account57", "account58", "account59", "account6", "account60", "account61", "account62", "account63", "account64", "account65", "account66", "account67", "account68", "account69", "account7", "account70", "account71", "account72", "account73", "account74", "account75", "account76", "account77", "account78", "account79", "account8", "account80", "account81", "account82", "account83", "account84", "account85", "account86", "account87", "account88", "account89", "account9", "account90", "account91", "account92", "account93", "account94", "account95", "account96", "account97", "account98", "account99", "amount", "amount-in", "amount-out", "amount1", "amount1-in", "amount1-out", "amount10", "amount10-in", "amount10-out", "amount11", "amount11-in", "amount11-out", "amount12", "amount12-in", "amount12-out", "amount13", "amount13-in", "amount13-out", "amount14", "amount14-in", "amount14-out", "amount15", "amount15-in", "amount15-out", "amount16", "amount16-in", "amount16-out", "amount17", "amount17-in", "amount17-out", "amount18", "amount18-in", "amount18-out", "amount19", "amount19-in", "amount19-out", "amount2", "amount2-in", "amount2-out", "amount20", "amount20-in", "amount20-out", "amount21", "amount21-in", "amount21-out", "amount22", "amount22-in", "amount22-out", "amount23", "amount23-in", "amount23-out", "amount24", "amount24-in", "amount24-out", "amount25", "amount25-in", "amount25-out", "amount26", "amount26-in", "amount26-out", "amount27", "amount27-in", "amount27-out", "amount28", "amount28-in", "amount28-out", "amount29", "amount29-in", "amount29-out", "amount3", "amount3-in", "amount3-out", "amount30", "amount30-in", "amount30-out", "amount31", "amount31-in", "amount31-out", "amount32", "amount32-in", "amount32-out", "amount33", "amount33-in", "amount33-out", "amount34", "amount34-in", "amount34-out", "amount35", "amount35-in", "amount35-out", "amount36", "amount36-in", "amount36-out", "amount37", "amount37-in", "amount37-out", "amount38", "amount38-in", "amount38-out", "amount39", "amount39-in", "amount39-out", "amount4", "amount4-in", "amount4-out", "amount40", "amount40-in", "amount40-out", "amount41", "amount41-in", "amount41-out", "amount42", "amount42-in", "amount42-out", "amount43", "amount43-in", "amount43-out", "amount44", "amount44-in", "amount44-out", "amount45", "amount45-in", "amount45-out", "amount46", "amount46-in", "amount46-out", "amount47", "amount47-in", "amount47-out", "amount48", "amount48-in", "amount48-out", "amount49", "amount49-in", "amount49-out", "amount5", "amount5-in", "amount5-out", "amount50", "amount50-in", "amount50-out", "amount51", "amount51-in", "amount51-out", "amount52", "amount52-in", "amount52-out", "amount53", "amount53-in", "amount53-out", "amount54", "amount54-in", "amount54-out", "amount55", "amount55-in", "amount55-out", "amount56", "amount56-in", "amount56-out", "amount57", "amount57-in", "amount57-out", "amount58", "amount58-in", "amount58-out", "amount59", "amount59-in", "amount59-out", "amount6", "amount6-in", "amount6-out", "amount60", "amount60-in", "amount60-out", "amount61", "amount61-in", "amount61-out", "amount62", "amount62-in", "amount62-out", "amount63", "amount63-in", "amount63-out", "amount64", "amount64-in", "amount64-out", "amount65", "amount65-in", "amount65-out", "amount66", "amount66-in", "amount66-out", "amount67", "amount67-in", "amount67-out", "amount68", "amount68-in", "amount68-out", "amount69", "amount69-in", "amount69-out", "amount7", "amount7-in", "amount7-out", "amount70", "amount70-in", "amount70-out", "amount71", "amount71-in", "amount71-out", "amount72", "amount72-in", "amount72-out", "amount73", "amount73-in", "amount73-out", "amount74", "amount74-in", "amount74-out", "amount75", "amount75-in", "amount75-out", "amount76", "amount76-in", "amount76-out", "amount77", "amount77-in", "amount77-out", "amount78", "amount78-in", "amount78-out", "amount79", "amount79-in", "amount79-out", "amount8", "amount8-in", "amount8-out", "amount80", "amount80-in", "amount80-out", "amount81", "amount81-in", "amount81-out", "amount82", "amount82-in", "amount82-out", "amount83", "amount83-in", "amount83-out", "amount84", "amount84-in", "amount84-out", "amount85", "amount85-in", "amount85-out", "amount86", "amount86-in", "amount86-out", "amount87", "amount87-in", "amount87-out", "amount88", "amount88-in", "amount88-out", "amount89", "amount89-in", "amount89-out", "amount9", "amount9-in", "amount9-out", "amount90", "amount90-in", "amount90-out", "amount91", "amount91-in", "amount91-out", "amount92", "amount92-in", "amount92-out", "amount93", "amount93-in", "amount93-out", "amount94", "amount94-in", "amount94-out", "amount95", "amount95-in", "amount95-out", "amount96", "amount96-in", "amount96-out", "amount97", "amount97-in", "amount97-out", "amount98", "amount98-in", "amount98-out", "amount99", "amount99-in", "amount99-out", "balance", "balance1", "balance10", "balance11", "balance12", "balance13", "balance14", "balance15", "balance16", "balance17", "balance18", "balance19", "balance2", "balance20", "balance21", "balance22", "balance23", "balance24", "balance25", "balance26", "balance27", "balance28", "balance29", "balance3", "balance30", "balance31", "balance32", "balance33", "balance34", "balance35", "balance36", "balance37", "balance38", "balance39", "balance4", "balance40", "balance41", "balance42", "balance43", "balance44", "balance45", "balance46", "balance47", "balance48", "balance49", "balance5", "balance50", "balance51", "balance52", "balance53", "balance54", "balance55", "balance56", "balance57", "balance58", "balance59", "balance6", "balance60", "balance61", "balance62", "balance63", "balance64", "balance65", "balance66", "balance67", "balance68", "balance69", "balance7", "balance70", "balance71", "balance72", "balance73", "balance74", "balance75", "balance76", "balance77", "balance78", "balance79", "balance8", "balance80", "balance81", "balance82", "balance83", "balance84", "balance85", "balance86", "balance87", "balance88", "balance89", "balance9", "balance90", "balance91", "balance92", "balance93", "balance94", "balance95", "balance96", "balance97", "balance98", "balance99", "code", "comment", "comment1", "comment10", "comment11", "comment12", "comment13", "comment14", "comment15", "comment16", "comment17", "comment18", "comment19", "comment2", "comment20", "comment21", "comment22", "comment23", "comment24", "comment25", "comment26", "comment27", "comment28", "comment29", "comment3", "comment30", "comment31", "comment32", "comment33", "comment34", "comment35", "comment36", "comment37", "comment38", "comment39", "comment4", "comment40", "comment41", "comment42", "comment43", "comment44", "comment45", "comment46", "comment47", "comment48", "comment49", "comment5", "comment50", "comment51", "comment52", "comment53", "comment54", "comment55", "comment56", "comment57", "comment58", "comment59", "comment6", "comment60", "comment61", "comment62", "comment63", "comment64", "comment65", "comment66", "comment67", "comment68", "comment69", "comment7", "comment70", "comment71", "comment72", "comment73", "comment74", "comment75", "comment76", "comment77", "comment78", "comment79", "comment8", "comment80", "comment81", "comment82", "comment83", "comment84", "comment85", "comment86", "comment87", "comment88", "comment89", "comment9", "comment90", "comment91", "comment92", "comment93", "comment94", "comment95", "comment96", "comment97", "comment98", "comment99", "currency", "currency1", "currency10", "currency11", "currency12", "currency13", "currency14", "currency15", "currency16", "currency17", "currency18", "currency19", "currency2", "currency20", "currency21", "currency22", "currency23", "currency24", "currency25", "currency26", "currency27", "currency28", "currency29", "currency3", "currency30", "currency31", "currency32", "currency33", "currency34", "currency35", "currency36", "currency37", "currency38", "currency39", "currency4", "currency40", "currency41", "currency42", "currency43", "currency44", "currency45", "currency46", "currency47", "currency48", "currency49", "currency5", "currency50", "currency51", "currency52", "currency53", "currency54", "currency55", "currency56", "currency57", "currency58", "currency59", "currency6", "currency60", "currency61", "currency62", "currency63", "currency64", "currency65", "currency66", "currency67", "currency68", "currency69", "currency7", "currency70", "currency71", "currency72", "currency73", "currency74", "currency75", "currency76", "currency77", "currency78", "currency79", "currency8", "currency80", "currency81", "currency82", "currency83", "currency84", "currency85", "currency86", "currency87", "currency88", "currency89", "currency9", "currency90", "currency91", "currency92", "currency93", "currency94", "currency95", "currency96", "currency97", "currency98", "currency99", "date", "date2", "description", "end", "skip", or "status"

csviftablenonempty

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./csviftablenonempty.csv.rules:2:1:
  |
2 | if,date,description,comment
  | ^
start of conditional table found, but no assignment rules afterward

csviftablevaluecount

hledger: Error: /Users/simon/src/hledger/hledger/test/errors/./csviftablevaluecount.csv.rules:4:1:
  |
4 | one,val1
  | ^
line of conditional table should have 2 values, but this one has only 1

csvnoinclude

hledger: Error: sorry, CSV files can't be included yet

csvskipvalue

hledger: Error: could not parse skip value: "badval"

csvstatusparse

hledger: Error: error: could not parse "badstatus" as a cleared status (should be *, ! or empty)
the parse error is:      1:1:
  |
1 | badstatus
  | ^
unexpected 'b'
expecting '!', '*', or end of input

csvstdinrules

hledger: Error: please use --rules-file when reading CSV from stdin

csvtwofields

hledger: Error: CSV record ["b"] has less than two fields

csvstdinrules

hledger: Error: please use --rules-file when reading CSV from stdin

EXAMPLES

Collected examples

Many example input files in journal and other formats can be found in examples/ in the hledger repo.

Sample journals

Synthetic data files like examples/100x100x10.journal are useful for benchmarks and testing. The numbers describe the number of transactions, number of accounts, and maximum account depth respectively. They are generated by tools/generatejournal.hs. They should get built automatically as needed, if not you can use make samplejournals:

$ just samplejournals
ghc tools/generatejournal.hs
[1 of 1] Compiling Main             ( tools/generatejournal.hs, tools/generatejournal.o )
Linking tools/generatejournal ...
tools/generatejournal 100 100 10 >examples/100x100x10.journal
tools/generatejournal 1000 1000 10 >examples/1000x1000x10.journal
tools/generatejournal 1000 10000 10 >examples/1000x10000x10.journal
tools/generatejournal 10000 1000 10 >examples/10000x1000x10.journal
tools/generatejournal 10000 10000 10 >examples/10000x10000x10.journal
tools/generatejournal 100000 1000 10 >examples/100000x1000x10.journal
tools/generatejournal 3 5 5 >examples/ascii.journal
tools/generatejournal 3 5 5 --chinese >examples/chinese.journal
tools/generatejournal 3 5 5 --mixed >examples/mixed.journal

Files

Some views and explanations of files in the hledger project, as of 2022-12.


hledger working copy

A full working copy of the official hledger repos is best laid out like this (manually; we currently don't use git submodules):

src/hledger/ - git clone https://github.com/simonmichael/hledger; cd hledger
  site/      - git clone https://github.com/simonmichael/hledger_site site
  finance/   - git clone https://github.com/simonmichael/hledger_finance finance

You don't need to clone all of these repos unless you are working in all of those areas.

The next two listings show the directories in the main and site repos:

main repo directories

The main repo contains the hledger-lib, hledger, hledger-ui, and hledger-web haskell packages, the hledger-install script, a collection of example data, some documentation and other support files.

src/hledger/
  .github/
    ISSUE_TEMPLATE/
    workflows/
      old/
  .sandstorm/
  bin/
  checks/
  doc/
    haskellerz/
    hcar/
    mockups/
    profs/
  docker-static-arm32v7/
  docker/
  examples/
    budgeting/
    csv/
    investing/
    invoicing/
      invoice-script/
      makefile/
    reports/
    systemd/
    templates/
      basic/
  hledger-install/
  hledger-lib/
    Hledger/
      Data/
        JournalChecks/
      Read/
      Reports/
      Utils/
    Text/
      Megaparsec/
      Tabular/
    other/
      ledger-parse/
        Ledger/
          Parser/
    test/
  hledger-ui/
    Hledger/
      UI/
    test/
  hledger-web/
    Hledger/
      Web/
        Handler/
        Settings/
        Widget/
    app/
    config/
    deploy/
    static/
      css/
      fonts/
      js/
    templates/
    test/
  hledger/
    Hledger/
      Cli/
        Commands/
    app/
    bench/
    embeddedfiles/
    shell-completion/
    test/
      addons/
      balance/
      cli/
      errors/
      i18n/
      import/
      journal/
        account-display-order/
          1/
      print/
      register/
  tools/

site repo directories

The site repo contains the website infrastructure, versioned snapshots of the user manuals, home page, cookbook and other docs.

src/hledger/site/
  css/
  js/
  src/
    1.0/
    1.1/
    1.10/
    1.11/
    1.12/
    1.13/
    1.14/
    1.15/
    1.16/
    1.17/
    1.18/
    1.19/
    1.2/
    1.20/
    1.21/
    1.22/
    1.23/
    1.24/
    1.25/
    1.26/
    1.27/
    1.28/
    1.3/
    1.4/
    1.5/
    1.9/
    dev/
    fonts/
    highslide/
      graphics/
        outlines/
    images/
      hledger-ui/
      hledger-web/
        normal/
        small/
  theme/
    css/

finance repo

The finance repo contains transaction journals and financial reports.

src/hledger/finance/
  Makefile
  README.md
  oc.accounts
  oc.csv
  oc.csv.rules
  oc.journal

hledger user scripts

Scripts for users are in bin/:

src/hledger/bin/
  README.md
  _hledger-chart.hs
  bashrc
  compile.sh
  csv.mk
  hledger-addon-example.hs
  hledger-balance-as-budget.hs
  hledger-check-fancyassertions.hs
  hledger-check-postable.hs
  hledger-check-tagfiles.cabal.hs
  hledger-check-tagfiles.hs
  hledger-combine-balances.hs
  hledger-git
  hledger-move.hs
  hledger-pijul
  hledger-print-location.hs
  hledger-simplebal
  hledger-smooth.hs
  hledger-swap-dates.hs
  paypaljson
  paypaljson2csv
  scripts.test
  watchaccounts

hledger developer tools

Scripts used by developers and maintainers tend to be in tools/:

src/hledger/tools/
  README
  changelog.hs
  commitlint
  criterionbench.hs
  dayssincetag.hs
  docshelltest.hs
  generatejournal.hs
  generatetimeclock.hs
  gtree
  hackageupload
  listbydeps.hs
  pandoc-dedent-code-blocks.lua
  pandoc-demote-headers.lua
  pandoc-drop-html-blocks.lua
  pandoc-drop-html-inlines.lua
  pandoc-drop-links.lua
  pandoc-drop-toc.lua
  pandoc-toc.lua
  pandoc-wiki-links.lua
  progressionbench.hs
  regressiontest.py
  release
  runhledgercov
  simplifyprof.hs
  trhsx

Sh/makefiles

Many developer tasks are automated via Justfile or Shake.hs also. Run just or ./Shake.hs to see help.

$ gtree '(^|/)((bsd)?m|sh)ake|justfile'
src/hledger/
  Justfile
  Makefile
  Makefile.helpsys
  Shake.hs
  bin/
    justfile
  doc/
    haskellerz/
      Makefile
  examples/
    csv/
      Makefile
    invoicing/
      pandoc-make-invoice/
        Makefile
    reports/
      Makefile
  hledger/
    shell-completion/
      BSDmakefile
      Makefile
    test/
      errors/
        Makefile

YAML files

Developer configuration often happens in .yaml or .yml files.

src/hledger/
  .hlint.yaml
  azure-pipelines.yml
  hie-other.yaml
  stack.yaml
  stack8.10.yaml
  stack9.0.yaml
  stack9.4.yaml
  .github/
    FUNDING.yml
    ISSUE_TEMPLATE/
      config.yml
    workflows/
      binaries-linux-arm32v7-static.yml
      binaries-linux-x64-static.yml
      binaries-mac-x64.yml
      binaries-windows-x64.yml
      test-linux-x64.yml
      old/
        release.yml
  hledger-lib/
    package.yaml
  hledger-ui/
    package.yaml
  hledger-web/
    package.yaml
    config/
      keter.yaml
      settings.yml
  hledger/
    package.yaml

Core docs

Core documentation which should stay closely synced with hledger's implementation (changelogs, user manuals, developer docs) is kept in the main repo.

  • Many directories have a README.md explaining their purpose and content.

  • Each hledger package, and the project itself, has a CHANGES.md changelog file.

  • hledger/hledger.m4.md, hledger-ui/hledger-ui.m4.md, hledger-web/hledger-web.m4.md are the user manuals, which get rendered as html, info, man and plain text. They are processed first with m4 for extra flexibility.

  • The hledger manual imports the subcommand docs from hledger/Hledger/Cli/Commands/*.md.

  • doc/ contains other developer docs.

src/hledger/
  CHANGES.md
  README.md
  .github/
    pull_request_template.md
    ISSUE_TEMPLATE/
      a-bug.md
      a-wish.md
  .sandstorm/
    README.md
    changelog.md
    description.md
  bin/
    README.md
  doc/
    ACHIEVEMENTS.md
    BENCHMARKS.md
    CHANGELOGS.md
    CODE.md
    COMMITS.md
    CONTRIBUTING.md
    CREDITS.md
    DOCS.md
    EXAMPLES.md
    FILES.md
    FINANCE.md
    ISSUES.md
    LINKS.md
    MAKE.md
    PULLREQUESTS.md
    RELEASING.md
    REPOS.md
    SHAKE.md
    TESTS.md
    VERSIONNUMBERS.md
    DEVWORKFLOWS.md
    github-release-doc.tmpl.md
    haskellerz/
      haskellerz.md
    hcar/
      HCAR-hledger-201611.md
  docker/
    README.md
  examples/
    invoicing/
      README.md
      invoice-script/
        README.md
        abinvoice.tmpl.md
      makefile/
        202001ab.md
        README.md
    systemd/
      hledger-web.service
      hledger.nginx
      readme.md
    templates/
      README.md
  hledger-install/
    README.md
  hledger-lib/
    CHANGES.md
    README.md
  hledger-ui/
    CHANGES.md
    README.md
    hledger-ui.m4.md
    test/
      uitest.md
  hledger-web/
    CHANGES.md
    README.md
    hledger-web.m4.md
  hledger/
    CHANGES.md
    README.md
    hledger.m4.md
    Hledger/
      Cli/
        Commands/
          Accounts.md
          Activity.md
          Add.md
          Aregister.md
          Balance.md
          Balancesheet.md
          Balancesheetequity.md
          Cashflow.md
          Check.md
          Close.md
          Codes.md
          Commodities.md
          Descriptions.md
          Diff.md
          Files.md
          Help.md
          Import.md
          Incomestatement.md
          Notes.md
          Payees.md
          Prices.md
          Print.md
          README.md
          Register.md
          Rewrite.md
          Roi.md
          Stats.md
          Tags.md
          Test.md
    shell-completion/
      README.md
    test/
      README.md
      errors/
        README.md

Additional docs

Additional docs (intro pages, faqs, tutorials, cookbook..) are kept in the site repo's src/ directory. index.md is the home page. SUMMARY.md defines the site structure.

Developer docs (UPPERCASE) and the latest dev manuals (dev/*.md) from the main repo are also symbolically linked there, so that they too appear on the website.

src/hledger/site/src/
  ACHIEVEMENTS.md
  BACKLOG.md
  BENCHMARKS.md
  CHANGELOGS.md
  CODE.md
  COMMITS.md
  CONTRIBUTING.md
  CREDITS.md
  DOCS.md
  ERRORS.md
  EXAMPLES.md
  FILES.md
  FINANCE.md
  ISSUES.md
  LINKS.md
  MAKE.md
  PULLREQUESTS.md
  RELEASING.md
  REPOS.md
  ROADMAP.md
  SHAKE.md
  SUMMARY.md
  TESTS.md
  VERSIONNUMBERS.md
  DEVWORKFLOWS.md
  accounting.md
  balancing-the-accounting-equation.md
  basics.md
  beancount.md
  budgeting-and-forecasting.md
  budgeting.md
  change-account-name-separator.md
  charts.md
  checking-for-errors.md
  command-line-completion.md
  common-journal-entries.md
  common-workflows.md
  conversion2.md
  cookbook.md
  create-a-journal.md
  currency-conversion.md
  dev-README.md
  dev.md
  dsq.md
  editors.md
  export.md
  faq.md
  features.md
  financerepo.md
  finfaq.md
  forecasting.md
  foreign-trip-expenses.md
  gain.md
  gnucash.md
  hledger-web-tips.md
  import-csv.md
  index.md
  install.md
  inventory.md
  investments.md
  invoicing.md
  ledger.md
  loans.md
  manuals.md
  mobile.md
  mockups.md
  multicurrency-tutorial.md
  project-accounting.md
  quicken.md
  2-minute-quick-start.md
  5-minute-quick-start.md
  release-notes.md
  report-examples.md
  rewrite-account-names.md
  rewrite-commodity-symbols.md
  roi.md
  save-frequently-used-options.md
  scripting.md
  scripts.md
  simons-setup.md
  snippets.md
  sponsor.md
  sqlite.md
  start.md
  support.md
  tags-tutorial.md
  time-planning.md
  time-to-money.md
  track-changes-with-version-control.md
  track-investments.md
  ui.md
  videos.md
  web.md
  1.0/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.1/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.10/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.11/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.12/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.13/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.14/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.15/
    csv.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.16/
    csv.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.17/
    csv.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.18/
    csv.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.19/
    csv.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.2/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.20/
    csv.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.21/
    hledger-ui.md
    hledger-web.md
    hledger.md
  1.22/
    hledger-ui.md
    hledger-web.md
    hledger.md
  1.23/
    hledger-ui.md
    hledger-web.md
    hledger.md
  1.24/
    hledger-ui.md
    hledger-web.md
    hledger.md
  1.25/
    hledger-ui.md
    hledger-web.md
    hledger.md
  1.26/
    hledger-ui.md
    hledger-web.md
    hledger.md
  1.27/
    hledger-ui.md
    hledger-web.md
    hledger.md
  1.28/
    hledger-ui.md
    hledger-web.md
    hledger.md
  1.3/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.4/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.5/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  1.9/
    csv.md
    hledger-api.md
    hledger-ui.md
    hledger-web.md
    hledger.md
    journal.md
    timeclock.md
    timedot.md
  dev/
    hledger-ui.md
    hledger-web.md
    hledger.md

Site config files

These help configure the website.

  • book.toml is the main config file for mdbook.

  • src/SUMMARY.md defines the site's pages and which ones appear in the sidebar (except for old manual versions; those are rendered separately).

src/hledger/site/
  Makefile
  book.toml
  css/
    site.css
  js/
    bootstrap.min.js
    jquery-1.11.0.min.js
    site.js
  src/
    SUMMARY.md
    highslide/
      highslide-ie6.css
      highslide.css
      highslide.js
  theme/
    book.js
    favicon.png
    favicon.svg
    highlight.css
    highlight.js
    index.hbs
    css/
      chrome.css
      general.css
      print.css
      variables.css

Finance

https://hledger.org/finance this page https://github.com/simonmichael/hledger_finance finance repo

Old:

Funding

My vision for the hledger project has always been for it to be "accountable" and "self-sustaining", possibly through new forms of incentivisation. Classic non-monetary FOSS communities are a beautiful and precious thing. Adding money can change their dynamic. Yet, we would enjoy having a lot more issues resolved, and a faster rate of progress. So we experiment, gently.

Currently we use bounties as a way to encourage resolution of issues. There are a few ways to do this:

  1. You or your organisation can offer a bounty simply by saying so on the issue.

  2. You can use Bountysource. A few hledger bounties have been completed there.

  3. You can use the new Open Collective process below.

Issues with bounties of any kind are marked with the bounty label. The Bounty Manager is @simonmichael.

New bounty process

It currently looks like this, and will evolve:

  • Issues are marked as bounties by @simonmichael. Feel free to suggest additional issues which should receive the bounty label.

  • Bounties are paid from the hledger project's public Open Collective fund. By contributing to the fund as an individual or organisation, you enable more bounties.

  • These OC bounties (unlike 1 and 2 above) have standard amounts. These may be adjusted over time, depending eg on the state of our funds. Our current bounty amounts are

    • level 1: 10 USD
    • level 2: 25 USD
    • level 3: 50 USD
  • When you complete a bounty, submit an expense to Open Collective, for whichever of the above bounty amounts you think appropriate, based eg on time or expertise spent, how much you need it, how much remains in our fund for other bounties, etc. This will be reviewed by OC and (maybe ?) @simonmichael. Successful claims, like donations, will appear in our public OC ledger.

Our bounty amounts are small, and nothing like professional rates in most countries, but they still establish a principle of sustainability, and help us to experiment. You are encouraged to claim your bounties, though you can also choose to transfer them to a new issue of your choice.

Investment Accounting Feature Ladder

When trying to understand or build accounting software for investment, for example in the context of Plain Text Accounting, things get complicated rather fast. The following ladder can be helpful when evaluating or building investment-related features in accounting software.

(hledger users: this is not documentation for hledger; if you're looking for that, try Cookbook > Investing and trading.)

Each rung of the ladder adds a new concept or feature, describing:

  • one small increment to user-visible functionality
  • any new accounting/investing concepts needed
  • any new simplifying assumptions and scope
  • any new changes known to be needed in internal capabilities

This is a start, suggestions welcome in #hledger or #plaintextaccounting matrix chats.

Assets

Track asset transactions and balances. Initial assumptions/constraints: assets are cash (in a single base currency) or simple investment commodities. (In this doc we'll call those "cash" and "commodities" respectively.)

Asset costs

Track purchase costs, in the base currency, of assets. An asset balance can have more than one cost. Allow reporting total cost of assets.

Asset values

Track fluctuating market prices for individual commodities. Allow reporting current market value of asset balances.

Report unrealised gains

Allow reporting the unrealised gain or loss of asset balances. Unrealised gains are the increase (gain) or decrease (loss) of the value of assets you are holding, since you acquired them. The difference between current value and your original cost.

Report realised gains

...

Issues

The hledger project's issue tracker is on github. It contains:

  • BUG issues - failures in some part of the hledger project (the main hledger packages, docs, website..)
  • WISH issues - feature proposals, enhancement requests
  • uncategorised issues - we don't know what these are yet
  • pull requests - proposed changes to code and docs

Here are some shortcut urls:

In 2017 we experimented with Github projects, in 2018 with Github milestones. Long ago we collected some wishlist items in a trello board (trello.hledger.org).

Open issues

By topic and type.

Labels

In the github issue tracker we use various https://github.com/simonmichael/hledger/labels, some which appear above, to categorise things like:

  • whether an issue is a bug (red) or a wish (pink)
  • which subcomponents (tools, commands, input/output formats) are involved (light blue)
  • which platforms are involved (light purple)
  • whether a bounty has been offered (bright green)
  • what is blocking an issue (grey)
  • bug impact and severity (light orange and light pink, see below)
  • miscellaneous things like security (bright red), regressions (black), release needed (orange)
  • etc.

These labels also get used as prefixes in commit messages, in issue/PR titles, etc.

Conventions

Some loose conventions:

Estimates

You might see some old experiments in estimate tracking, where issue titles have a suffix noting estimated and spent time. Basic format: [ESTIMATEDTOTALTASKTIME|TIMESPENTSOFAR]. Examples:

  • [2] two hours estimated, no time spent
  • [..] half an hour estimated (a dot is ~a quarter hour, as in timedot format)
  • [1d] one day estimated (a day is ~4 hours)
  • [1w] one week estimated (a week is ~5 days or ~20 hours)
  • [3|2] three hours estimated, about two hours spent so far
  • [1|1w|2d] first estimate one hour, second estimate one week, about two days spent so far

Estimates are always for the total time cost (not time remaining). Estimates are not usually changed, a new estimate is added instead. Numbers are very approximate, but better than nothing.

Prioritising

https://lostgarden.home.blog/2008/05/20/improving-bug-triage-with-user-pain/ describes an interesting method of ranking issues by a single "User Pain" metric. What adaptation of this might be useful for the hledger project ?

Here's a simplified version, currently being tested in the hledger issue tracker:

Two labels can be applied to bug reports, each with levels from 1 to 5:

Impact

Who may be impacted by this bug ?

  • impact1: Affects almost no one.
  • impact2: Affects packagers or developers.
  • impact3: Affects just a few users.
  • impact4: Affects more than a few users.
  • impact5: Affects most or all users.

Severity

To people impacted, how serious is this bug ?

  • severity1: Cleanliness/consistency/developer bug. Only perfectionists care.
  • severity2: Minor to moderate usability/doc bug, reasonably easy to avoid or tolerate.
  • severity3: New user experience or installability bug. A potential user could fail to get started.
  • severity4: Major usability/doc bug, crash, or any regression.
  • severity5: Any loss of user's data, privacy, security, or trust.

User Pain

The bug's User Pain score is Impact * Severity / 25, ranging from 0.04 to 1.

Then, practices like these are possible:

  • All open bugs can be listed in order of User Pain (AKA priority).
  • Developers can check the Pain List daily and fix the highest pain bugs on the list.
  • The team can set easy-to-understand quality bars. For example, they could say “In order to release, we must have no open bugs with more than 15 pain.”
  • If there are no bugs left above the current quality bar, they can work on feature work.
  • If a bug is found that will take more than a week to fix, it can be flagged as a ‘killer’ bug, for special treatment.

Reducing bugs and regressions

Some ideas in 2024-01:

  • Maintain ratio of user-visible bugfixes to new features, eg above 10:1 (a new master merge test, human checked)
  • A release cycle with no new features
  • Alternate bugfix and feature release cycles
  • Set bug count targets
  • Label all issues for impact/severity/user pain; set max user pain targets
  • Gate releases on user pain targets or other bug metrics
  • Document and follow more disciplined bug triage/fixing methods
  • Identify every new bug early as a regression/non-regression
  • Prioritise rapid fixing and releasing for regressions / new bugs
  • Cheaper, more frequent bugfix releases
  • More intentional systematic tests ? Analyse for weak spots ?
  • Property tests ?
  • Internal cleanup, architectural improvements, more type safety ?
  • Custom issue dashboards (HTMX on hledger.org ?)
  • Public list / QA dashboard
  • Grow a QA team

Older ideas

  • Custodians for particular components/topics ("If you are interested in helping with a particular component for a while, please add yourself as a custodian in the Open Issues table. A custodian's job is to help manage the issues, rally the troops, and drive the open issue count towards zero. The more custodians, the better! By dividing up the work this way, we can scale and make forward progress.")

Just, make, and Shake

After many years of relying on GNU Make for automating project tasks, we now primarily use just instead. just is better enough, and clean consolidated efficient project automation is so valuable, that this is worthwhile - even though just requires installing and lacks Make's file dependency tracking, for now.

Justfile in the main repo's top directory is the starting point for scripts automating hledger project tasks. Install and run just to list them. I suggest using a shorter command alias j. Eg:

$ alias j=just
$ j             # list all scripts
$ j h test      # list scripts mentioning "test"

Makefiles are still used in a number of subdirectories, like site/ and finance/.

Shake

Shake.hs in the main repo's top directory complements the Justfile. Tasks requiring file dependencies and/or more complex code, such as building documentation and the web site, are usually scripted here, often with a corresponding alias in Justfile. Eg:

$ ./Shake.hs   # compile it for speed and git branch independence. Or: just Shake
$ ./Shake      # list scripts
$ j site       # runs the `just site` which calls `./Shake site` to do the work
$ j -n site    # just --dry-run, show the commands that `just site` will run

tools

Additional helper scripts and tools are kept in the tools/ subdirectory.

MOCKUPS

Mockups, draft docs and notes exploring possible future features. See also https://github.com/simonmichael/hledger/tree/master/doc/mockups

Lot terminology

Some investment-related terminology, as we use it here and in the PTA world:

  • "Investment" - something whose value fluctuates while you hold it.

  • Acquiring, disposing - receiving and getting rid of investments, whether by purchase, exchange, gift, stock options..

  • Augmenting, reducing - the same thing; terminology used in Beancount docs. Most often the investment is an asset and acquiring/augmenting increases a positive balance, but with other kinds of investments (options..) it might decrease a negative balance. Acquiring/augmenting increases your exposure (risk), disposing/reducing reduces it.

  • Lot - a quantity of an investment purchased at a specific time and cost. It may also have descriptive note attached. With many investments, lots must be tracked individually for tax reporting.

  • Cost basis - a lot's acquisition cost. More generally, the combination of acquisition time, cost, and note if any.

  • Capital gain/loss - your net profit or loss arising from the change in value of an investment since you acquired it. Some times abbreviated as "gains" in these docs. While you are holding the investment, you have unrealised gains, which fluctuate along with the market value. Once you dispose of it, you have realised gains. Capital gain/loss has tax consequences.

  • Reduction strategy, lot selection - the order in which lots are reduced, eg when you are selling a stock or gifting some cryptocurrency, which ones do you reduce first ? Common strategies: FIFO (first in first out), LIFO (last in first out), and Specific Order (a custom order, which should be recorded). The reduction strategy affects capital gains now and later, and has tax consequences. Sometimes you can choose it, at other times it is mandated by the tax authorities.

Lot ideas

2023-01 Some examples/brainstorming of lot notations and functionality.

I believe one could emulate most of ledger/beancount's lot tracking/selection with simpler syntax - just @, with less or no need for {} (curly brace syntax).

Explicit lot accounts

Eg here, using explicit subaccounts to track lots, no {} is needed.:

2022-01-01 buy at 10
  assets:aaa:_20220101     10 AAA @ $10
  assets:cash           $-100
    
2022-02-01 buy at 12
  assets:aaa:_20220201     10 AAA @ $12
  assets:cash           $-120
    
2022-03-01 sell at 20
  assets:aaa:_20220101    -10 AAA @ $10  ; original cost basis
  assets:aaa:_20220201     -5 AAA @ $12
  assets:cash            $300
  revenues:gains        $-140

Inferring cost from lot account

Assuming each lot subaccount holds only one lot, the cost basis could be recalled automatically when selling, though it's less readable:

2022-01-01 buy at 10
  assets:aaa:_20220101     10 AAA @ $10
  assets:cash           $-100
    
2022-02-01 buy at 12
  assets:aaa:_20220201     10 AAA @ $12
  assets:cash           $-120
    
2022-03-01 sell at 20
  assets:aaa:_20220101    -10 AAA  ; @ $10 implied
  assets:aaa:_20220201     -5 AAA  ; @ $12 implied
  assets:cash            $300
  revenues:gains        $-140

Cost in lot account name

Cost basis could also be indicated in the subaccount name:

2022-01-01 buy at 10
  assets:aaa:_20220101_$10     10 AAA @ $10
  assets:cash               $-100
    
2022-02-01 buy at 12
  assets:aaa:_20220201_$12     10 AAA @ $12
  assets:cash               $-120
    
2022-03-01 sell at 20
  assets:aaa:_20220101_$10    -10 AAA  ; @ $10 implied, now more clear
  assets:aaa:_20220201_$12     -5 AAA
  assets:cash                $300
  revenues:gains            $-140

Automatic lot accounts

Lot subaccounts could be created automatically, without having to write them; and could be used to select lots when withdrawing:

2022-01-01 buy at 10
  assets:aaa                 10 AAA @ $10  ; creates _20220101_$10 subaccount
  assets:cash             $-100
    
2022-02-01 buy at 12
  assets:aaa                 10 AAA @ $12  ; creates _20220201_$12
  assets:cash             $-120
    
2022-03-01 sell at 20
  assets:aaa:_20220201_$12  -10 AAA  ; select lot by subaccount
  assets:aaa:_20220101_$10   -5 AAA  ; LIFO order here
  assets:cash              $300
  revenues:gains          $-130

Implicit lots

Or there could be no lot subaccounts, just lots tracked implictly by the tool, with special commands to view them, as in ledger/beancount:

2022-01-01 buy at 10
  assets:aaa                 10 AAA @ $10  ; creates an implicit lot
  assets:cash             $-100
    
2022-02-01 buy at 12
  assets:aaa                 10 AAA @ $12  ; view lots with bal --lots
  assets:cash             $-120

Reduction strategy

Whether explicit, automatic or implicit, lots could be selected automatically according to some reduction strategy, specified eg with a tag:

2022-03-01 sell at 20, FIFO
  assets:aaa                -15 AAA  ; reduce lots FIFO by default
  assets:cash              $300
  revenue:gains                      ; $-140 calculated
2022-03-01 sell at 20, LIFO
  assets:aaa                -15 AAA  ; reduce:LIFO
  assets:cash              $300
  revenue:gains                      ; $-130 calculated

The above are easy to enter but less informative and hard to calculate by eye; you could use the tool to convert to a more explicit entry:

2022-03-01 sell at 20, FIFO
  assets:aaa                -10 AAA @ $10
  assets:aaa                 -5 AAA @ $12
  assets:cash              $300
  revenue:gains           $-140
2022-03-01 sell at 20, LIFO
  assets:aaa                -10 AAA @ $12
  assets:aaa                 -5 AAA @ $10
  assets:cash              $300
  revenue:gains           $-130

Lot selection syntax

If lots are implicit, ie there are no subaccounts by which to select them, some special syntax is needed to allow identifying them individually by cost, date, and/or note. This could be {}, [], tags, or something new. Eg:

2022-03-01 sell at 20, taking 3 alternately from each lot
  assets:aaa                 -3 AAA {@ $10}                         ; lot 1
  assets:aaa                 -3 AAA {2022-02-01}                    ; lot 2
  assets:aaa                 -3 AAA {buy at 10}                     ; lot 1
  assets:aaa                 -3 AAA {@ $10, 2022-02-01, buy at 12}  ; lot 2
  assets:aaa                 -3 AAA                                 ; lot-date:2022-01-01, lot-cost:$10, lot-note:buy at 10, (lot 1)
  assets:cash              $300
  revenue:gains           $-138

Use of curly braces

I don't see the need to use {} as much as Ledger/Beancount do. In particular, Ledger/Beancount's {} syntax allows creating a lot with a cost basis different from what it cost you in the transaction acquiring it. What is the real need for this, and how often is it needed ?

It's not needed eg when buying a commodity at a rate different from the market rate; you can do:

2022-01-01 receive AAA, currently worth $10, with effective cost to us of ~$11 because of fees
  revenues:usd              -10 AAA @ $10
  expenses:fees               1 AAA
  equity:basis adjustment    -1 AAA
  assets:cash                 9 AAA @ $11.111

commodity $0.00  ; help hledger balance the above

Investments vs one-time transactions

Not yet mentioned: some commodities/balances fluctuate in value while you hold them (eg an investment) and others are a one-time conversion (eg buying foreign currency at the airport).

@ can be used for both of these, it's essentially a matter of which cost you calculate with when disposing:

2022-01-01 buy at 10, hold with fluctuating value
  assets:aaa                 10 AAA @ $10     ; today's acquisition cost
  assets:cash             $-100

2022-03-01 sell at 20, with capital gain/loss
  assets:aaa                -10 AAA @ $10     ; original acquisition cost
  assets:cash              $200
  revenue:gains           $-100
2022-01-01 exchange SEK for USD, one-time conversion
  assets:cash              -100 SEK
  assets:cash                10 USD @ 10 SEK  ; today's conversion cost

2022-03-01 exchange back to SEK, one-time conversion
  assets:cash               -10 USD @ 11 SEK  ; today's conversion cost
  assets:cash               110 SEK

I believe @ and {} were intended to/can/do distinguish between these. If using only @ there needs to be some other mechanism to indicate fluctuating value vs one-time conversion, or so it seems - eg an annotation on the transaction, the account, or the commodity.

Price syntax

In Ledger and hledger

  • In the journal, a P DATE COMMODITY AMOUNT directive some commodity's market price in some other commodity on DATE. (A timestamp may be added, but is ignored.)

  • In a posting, AMT @ UNITPRICE declares the per-unit price that was used to convert AMT into the price's commodity. Eg: 2A @ 3B records that 2A was posted, in exchange for 6B.

  • @@ TOTALPRICE is another form of @, sometimes more convenient. Eg: 2A @@ 5.99B records that 2A was posted in exchange for 5.99B.

In Ledger

  • @ UNITPRICE Any use of @ also generates an implicit P directive. Eg:

    2019/1/1
      a  2A @ 3B
      b
    

    in the journal is equivalent to writing

    2019/1/1
      a  2A @ 3B
      b
    
    P 2019/1/1 A 1.5B
    
  • {UNITPRICE}

  • {=FIXEDUNITPRICE}

The following are variants of the above; they work the same way except that you write the total instead of the unit price:

  • @@ TOTALPRICE
  • {{TOTALPRICE}}
  • {{=FIXEDTOTALPRICE}}

In hledger

  • @ does not generate a market price
  • {} and {=} are ignored

Capital gains

A model for capital gains

Capital gain/loss (when the value of assets you hold increases/decreases due to market price fluctuations) - is an important topic, since it can generate tax liability.

Here is a description of how it works, intended for both users and builders of accounting software (especially, plain text accounting software). (I'm a software engineer, not an accountant. In places there may be better accounting terms I'm not familiar with yet.)

  • lots/units - A quantity of some commodity, acquired at a certain price on a certain date, is called a lot, or unit. (I'm not sure which is the most standard term. Using lot for now.)

  • Since you might have purchased the lot on a stock exchange, received it as a gift, or something else, we'll call this event lot acquisition, on the acquisition date.

  • Later you might sell the lot for cash, or exchange it for something else, or gift it. We'll call this lot disposal.

  • You might have paid current market value for the lot, or you might have paid less or more than that. We'll call what you paid/exchanged the acquisition amount.

  • I think the acquisition amount is also called the basis or cost basis. Or possibly the current market value is the basis, regardless of what you paid. Perhaps it depends. To be clarified. The basis at which you acquired a lot is important.

  • After acquisition, while you are still holding the lot, if the market value of that commodity goes up (or down), your potential return from disposing of the lot increases (or decreases). This is known as capital gain (or loss) (we'll just call it "capital gain"). At this stage, the gain is only "on paper", so it is called unrealised capital gain (URG). This is not considered revenue, or taxable.

  • It's common to be holding multiple lots, perhaps many, even in a single account. Eg, say you buy a small amount of some stock or cryptocurrency each week. Each purchase adds a new lot to your assets. We'll call this a multi-lot balance, or balance.

  • URG is calculated for a lot at a certain point in time. Likewise for a multi-lot balance.

  • realised capital gain

  • lot withdrawal strategies

  • specific identification

Capital gains in hledger

  • postings can have multiple commodities and multiple prices; each of these parts is a deposit or withdrawal to the account

  • -- | Given a list of amounts all in the same commodity, interprets them
    -- as a sequence of lot deposits (the positive amounts) and withdrawals
    -- (the negative amounts), and applies them in order using the FIFO
    -- strategy for withdrawals, then returns the resulting lot balance (as
    -- another, shorter, list of amounts).
    sumLots :: [Amount] -> [Amount]
    

Ease of getting started

What could make getting started substantially easier ?

  • Official CI-generated binaries for all major platforms
  • Builtin access to docs in web format

Web docs

Provide the embedded user manuals as HTML also. Eg:

  • hledger help --html # temporary static html files
  • hledger help --web # serve from local hledger-web instance if installed
  • hledger help --site # on hledger.org
  • hledger-ui ? h/w/s # same as above
  • hledger-web -> help # served from hledger-web

Config file

Name: hledger.conf (and possibly ~/.hledger.conf as well).

  • easy to say and spell
  • good highlighting support in editors

Format: toml/ini-ish format, but customised for our needs (if necessary).

Example:

# hledger.conf

[defaults]
# Set options/arguments to be always used with hledger commands.
# Each line is: HLEDGERCMD ARGS, or: hledger ARGS
hledger -f hledger.journal
bal -M --flat -b lastmonth
ui --watch
web -V
help --html

[commands]
# Define aliases for custom hledger commands.
# Each line is: CMDALIAS = HLEDGERCMD ARGS
assets = bal -M ^assets\b
liab   = bal -M ^liabilities\b

# Or use colon, like make ?
bs2:   bs --no-total date:thisyear

# Or just whitespace, like hledger csv rules ?
smui   ui ^sm\b

# Allow arbitrary shell commands ?
2019:    hledger -f 2019.journal
jstatus: git status -sb -- *.journal

# Allow multi-command shell scripts, with optional help string ?
bsis:
  "Show monthly balance sheet and income statement"
  hledger bs -M
  echo
  hledger is -M
  echo

Loaded:

  • at startup and ideally:
  • hledger-web: on each page load if changed, like journals
  • hledger-ui --watch: on change, like journals

Location:

Search a number of locations in order. Values from multiple files are combined, with later files taking precedence.

User config file: should it be "modern" ~/.config/hledger.conf or "old/simple" ~/.hledger.conf ? One or the other may be preferred/easier/more portable. If we support both, should it be one or the other, or both ?

Parent directory config files: we'd probably like to recognise config files in parent directories. How far up should we look - to the root dir ? to the user's home dir ? and if not under the user's home dir, don't look up at all ? to the nearest VCS working directory root ?

This would be the simplest comprehensive scheme: use all of

  1. ~/.config/hledger.conf
  2. ~/.hledger.conf
  3. hledger.conf in all directories from / down to the current directory

Eg: running hledger in /home/simon/project/finance would combine any of the following which exist:

  • ~/.config/hledger.conf
  • ~/.hledger.conf
  • /hledger.conf
  • /home/hledger.conf
  • /home/simon/hledger.conf
  • /home/simon/project/hledger.conf
  • /home/simon/project/finance/hledger.conf

Cf #1353

User-visible changes when going from 1.20.4 to master:

-B/--costNow a primary flag.
--value=costNow an alias for -B/--cost, and deprecated.
--value=cost,COMMNo longer supported, suggests -B --value=X,COMM.
--value=endWith --change, shows change of end values instead of end value of change.
--value=then approximates and hopefully is preferable to the old behaviour.

Meaning of the cost/valuation short flags in master:

Short flagEquivalent to
-B--cost
-V--value=then (soon)
-X/--exchange COMM--value=then,COMM (soon)

Valuation examples

Minimal example for testing some valuation behaviours discussed in #1353. See Balance report valuation above.

; every ~15 days: one A is purchased, and A's market price in B increases.

2020-01-01
  (a)  1 A

2020-01-15
  (a)  1 A

2020-02-01
  (a)  1 A

2020-02-15
  (a)  1 A

P 2020-01-01 A  1 B
P 2020-01-15 A  2 B
P 2020-02-01 A  3 B
P 2020-02-15 A  4 B

Old balance --change --value=end behaviour: shows period-end value of period's balance change:

$ hledger-1.20.4 bal -M --value=end  # --change is the default
Balance changes in 2020-01-01..2020-02-29, valued at period ends:

   || Jan  Feb 
===++==========
 a || 4 B  8 B 
---++----------
   || 4 B  8 B 

New balance --change --value=end behaviour in master: shows change between period-end-valued period-end balances:

$ hledger-master bal -M --value=end
Period-end value changes in 2020-01-01..2020-02-29:

   || Jan   Feb 
===++===========
 a || 4 B  12 B 
---++-----------
   || 4 B  12 B 

balance --value=then is also supported in master: shows sum of postings' then-values in each period:

$ hledger-master bal -M --value=then
Balance changes in 2020-01-01..2020-02-29, valued at posting date:

   || Jan  Feb 
===++==========
 a || 3 B  7 B 
---++----------
   || 3 B  7 B 

Pull requests

Most contributed hledger code (and some of the project maintainer's code) is submitted and reviewed via Github pull requests. Here are some tips for contributing PRs to hledger.

Code review is important

We aim to improve and sustain hledger's quality and maintainability over the long term.

Many PRs, especially small ones, and even some big ones, can be merged quickly. We love merging PRs quickly.

Some bigger or more risky PRs can require substantial review, discussion, changes, or re-submission. Sometimes this is a bigger task than the coding. Much valuable design, quality control, and knowledge sharing happens at this time.

Some PRs ultimately get rejected, but their discussion and exploration can still be a useful contribution. We very much want to avoid wasted work, but it occasionally happens. Our process is evolving and imperfect. All of this is a normal part of software development.

We hope you'll see it as a golden opportunity to collaborate with experts, share and receive knowledge, refine your design/documentation/code, and practice real-world development and communication skills. Patience and persistence pays.

The pull request

A PR should have a clear purpose, documented in its description. Mention any #ISSUENOs addressed.

Don't tackle too much at once. Smaller/more focussed PRs can be reviewed quicker and accepted (or rejected) quicker.

Consider showing a draft of documentation first (more on this below).

The commit(s)

Commits should be easy to review. Ideally each commit is complete, and has a single clear purpose, which should be documented in the summary (and long description, if needed). #ISSUENOs can be mentioned in summary/description too when appropriate.

Within the above constraint, fewer, larger commits are preferred.

Keep in mind that commit messages are valuable documentation for future developers and troubleshooters. They are also the starting point for package changelogs and hledger release notes. High-quality commit messages makes the release process quicker, and the resulting docs better.

User-impacting commits should mention the user-visible changes, and be described in user-relevant language. Library-user-impacting commits, eg API changes, ideally will also be called out, and can described in more technical language. Commits affecting hledger internals are less important, but you may notice some adhoc conventions if you browse the history. In particular, you can optionally prefix the summary with short component codes (cf Issues) to facilitate history reading and changelog/release note production.

Rewrite and force-push your commits freely (rebase -i, push -f) to clean them up. Unless we decide to squash the PR into one commit, your commits will become part of hledger's history "for all time", so think about future developers trying to understand them, git bisect, etc.

Rebase your commits against latest master for easiest review. Especially if they start to conflict.

We like to use some conventions in commit messages when it makes sense. These aren't mandatory, but appreciated:

  • prepend a label prefix, eg cli: or journal: , for clarity and to help with changelog production
  • prepend a semicolon (;) to indicate commits that
    • need not trigger a CI workflows, reducing wasteful carbon emissions
    • and probably need not be mentioned in changelogs/release notes

The docs

PRs should include appropriate updates to reference documentation, unless otherwise agreed. Typically this means the manual source files (hledger*/hledger*.m4.md). It can also involve command line option names and descriptions, other --help output, hledger's commands list, hledger-ui's help dialog, hledger-web's help dialog, etc. Sometimes it means the developer docs, at least the ones in the main repo (READMEs).

Reviewers can understand your PR more efficiently once proposed doc changes are provided, and may postpone it otherwise. We are happy to help with the docs if needed - just ask.

Updating rendered manuals (hledger.{1,info,txt,md,html}) is not required, and probably best avoided to reduce conflicts. Updating other docs such as tutorials, how-tos, examples, or screenshots is not required, though it's welcome (may be in a different repo).

Documentation first

hledger follows documentation-driven design. It is in fact highly effective, and highly recommended, to write the new docs (help text/user manual/haddocks/developer README..) before writing any code. You can share a rough draft on IRC, on the mail list, in an issue comment, or in a "WIP" PR starting with just the proposed docs commit.

This is often the quickest road to getting something merged into hledger. hledger's many parts interact in surprisingly complex ways. The documentation-driven working style lets us discuss, clarify and reach a good-enough consensus economically, after which coding/review/acceptance can go quicker.

Neil Mitchell’s Blog - The One PR Per Day Rule

hledger

Robust, intuitive plain text accounting

license on hackage github issues

Welcome! This a brief intro to hledger. For a more detailed version, see the home page: https://hledger.org

hledger is lightweight, cross platform, multi-currency, double-entry accounting software. It lets you track money, investments, cryptocurrencies, invoices, time, inventory and more, in a safe, future-proof plain text data format with full version control and privacy.

hledger aims to help both computer experts and regular folks gain clarity in their finances and time management. Though the UIs are basic, hledger can model any accounting situation and provide precise, clear reports. It is reliable, quick, and backed by the highly supportive Plain Text Accounting ecosystem. Using it is an excellent way to learn double entry accounting.

Compared to other PTA apps, hledger is actively maintained, with regular releases, and a strong focus on being easy to use and practical for everyday accounting.

More features:

  • Installs easily on unix, mac or windows
  • Complete, built-in documentation in multiple formats, beginner videos, tutorials etc.
  • Multiple UIs: command-line, terminal, web, mobile, editors/IDEs
  • Good at importing and exporting CSV; also outputs text/HTML/JSON/SQL
  • A robust, general, well-specified multi-currency accounting engine
  • Fast, analysing 25k transactions per second on a macbook air m1
  • Accurate to 255 decimal places
  • Supports your preferred account names, currencies, number formats
  • Inspired by and partly compatible with Ledger CLI; interconvertible with Beancount
  • Scriptable by CLI, HTTP or API, with plenty of examples
  • Clean Haskell codebase, continually improved since 2007, with $100 regression bounties
  • Free software licensed under GPLv3+.

Examples

I use hledger to:

  • track income and spending, sometimes with budgets
  • see time reports by day/week/month/project
  • track reimbursables, invoices and payments
  • predict cashflow and account balances
  • get accurate numbers for tax filing
  • research past events

Here's an example of the journal file format:

2022-01-01 opening balances as of this date
    assets:bank:checking                $1000
    assets:bank:savings                 $2000
    assets:cash                          $100
    liabilities:creditcard               $-50
    equity:opening/closing balances

2022-01-15 market
    expenses:food             $50
    assets:cash              $-50

2022-02-01 GOODWORKS CORP
    assets:bank:checking           $1000
    income:salary                 $-1000

and some simple reports:

$ hledger bs
Balance Sheet 2022-02-15

                        || 2022-02-15 
========================++============
 Assets                 ||            
------------------------++------------
 assets:bank:checking   ||      $2000 
 assets:bank:savings    ||      $2000 
 assets:cash            ||        $50 
------------------------++------------
                        ||      $4050 
========================++============
 Liabilities            ||            
------------------------++------------
 liabilities:creditcard ||        $50 
------------------------++------------
                        ||        $50 
========================++============
 Net:                   ||      $4000 
$ hledger is --monthly                                            
Income Statement 2022-01-01..2022-02-28                                               
                                                                                      
               ||  Jan    Feb                                                         
===============++=============                                                        
 Revenues      ||                                                                     
---------------++-------------                                                        
 income:salary ||    0  $1000                                                         
---------------++-------------                                                        
               ||    0  $1000                                                         
===============++=============                                                        
 Expenses      ||                                                                     
---------------++-------------                                                        
 expenses:food ||  $50      0                                                         
---------------++-------------                                                        
               ||  $50      0                                                         
===============++=============                                                        
 Net:          || $-50  $1000                                                         

More examples and screenshots: https://hledger.org/#how-to-get-started

Funding

hledger is brought to you by Simon Michael, 140+ contributors, and the generous financial sponsors below.

After enjoying some personal or organisational success with hledger, you might want to become one of them, to help support this work. It's easy! Please see https://hledger.org/sponsor.html for details.

Organisational sponsors

Individual sponsors

REGRESSIONS

Short url for this page: hledger.org/regressions

A regression is "something that used to work, that broke", or "an unintended not-good change" in a released version of hledger.

We don't like regressions. We want to detect them quickly, repair them quickly, and reduce their frequency.

Regression bounty

You can help! Since 2021-06-14 we have offered a $100 bounty for each new regression reported in hledger releases. Since 2024-01-01, the bounty is split: $50 to the finder and $50 to the fixer (can be the same person, can't be the breaker).

To claim the bounty:

  1. Discover a new regression yourself (and don't be the one who caused it)
  2. Report it in the hledger bug tracker
  3. Wait for the issue manager (SM) confirm it with the regression label
  4. And/or, fix a regression yourself.
  5. Send an expense reimbursement request to our Open Collective. Be aware this might reveal your real name, on opencollective.com and here on hledger.org. Here's the suggested procedure for fastest processing:
    • click Submit Expense (if you are logged in, it will be under the ACTIONS menu)
    • choose Invoice
    • choose a Payout method; click Next
    • Expense title: "Regression finder bounty for #NNNN" (or "fixer" or "finder & fixer")
    • Description: "Found ISSUEURL, WORKDATE(S)" (or "Fixed ...")
    • Date: today's date
    • Amount: USD 50 (or 100 if you found and fixed). Or if you choose to receive another currency, convert from USD with that day's conversion rate, and mention the rate in Description.
    • Next, Submit Expense
  6. Announce on the issue page or in chat that you've submitted (to help ensure it is not overlooked)
  7. Wait for the finance manager (SM) to approve it. This should not take more than a day or two.
  8. Then wait for Open Collective to pay it. Payouts happen twice a week.

Regressions reported

hledger version, bug reportReporterBounty paid on
1.19 2020-09-01--
#1568jolmgpre bounty
#1688Simon MichaelN/A
#1698David Lowe2021-09-18
#1745Arne Schlüter2021-11-02
#1800Chuck Holmes2022-01-21
1.20 2020-12-05--
#1439apauleypre bounty
#1468Simon MichaelN/A
1.20.3 2021-01-14--
#1566benwebberpre bounty
1.21 2021-03-10--
#1508edlangloispre bounty
#1523Simon MichaelN/A
#1526lestephanepre bounty
#1527lestephanepre bounty
#1656Stephen Morgan2021-08-22
1.22 2021-07-03--
#1597Simon Michael2021-07-08
#1607Simon Michael2021-07-16
#1625Julian Klode2021-07-30
#1736Romain Gehrig2021-11-14
#1851Eric Langlois2022-04-11
1.22.1 2021-08-02--
#1638Yann Büchau2021-08-03
#1642Simon MichaelN/A
1.23 2021-09-21--
#1933Simon Michael2022-09-14
#2071William Pierce2024-04-02
1.24 2021-12-01--
#1782Simon MichaelN/A
1.25 2022-03-04--
#2032Simon Michael2023-05-03
#2196Pranesh Prakash
1.27 2022-09-01--
#1932Andras Fabian2022-09-15
#2018Allan Odgaard2023-03-28
1.29 2023-03-11--
#2012Simon MichaelN/A
#2020Pablo Mora2023-03-31
#2023Simon Michael2023-04-06
#2034Simon MichaelN/A
#2045Pranesh Prakash2023-10-17
#2153markokocic2024-01-25, $50 donated
1.30 2023-06-01--
#2072, #2137, #2150Simon Michael, usaAmch, ipvych
1.31 2023-09-03--
#2091Petr Slansky2023-10-16
#2115pepe_pecas2023-12-15, $100 donated
1.32 2023-12-01--
#2125Simon MichaelN/A
#2127rajeevn1
#2130Simon MichaelN/A
#2134pepe_pecas2023-12-15, $100 donated
#2156ishmaelavila

RELEASING

Notes for hledger release managers and maintainers.

Goals

2024

  • Make releasing easier

2023

  • Make releasing easyier

2022

  • Update/consolidate release process docs
  • Establish routine monthly release cadence
  • Make releasing easy

Release types

hledger major releases happen each quarter, normally at the start of the third month. Bugfix releases follow when needed. Preview releases may happen in the other months if wanted.

Major release
A.B
Bugfix release
A.B.C
Fixup release
A.B.C.D
Preview release
A.B.99.D
Contains:New features, breaking changesOnly bug fixesTrivial packaging fixes, no software changesEarly snapshot of the next major release
When:Third month of quarter: March, June, September, DecemberWhen neededNeverFirst & second months of quarter
Deliverables:
Changelogs
Github release
Binaries
Hackage release
Install page
hledger-install
Regression bounty
Release notes
Web manuals
Announcements

Release manager activities

These have complex interdependencies and sequencing constraints. Chunk, separate, routinise, document and automate them as far as possible.

Softwareselecting changes, packages, release dates; coordinating contributions; ensuring release readiness
Branch Managementcoordinating main and release branch, local and remote repos, CI branches
Version Bumpingchoosing and applying new version numbers and related things like tags, github releases, urls, ghc and dep versions, stackage resolvers, everywhere needed
Docscommand help, manuals, changelogs, release notes, github release notes, install page, install scripts, announcements, process docs
Testinglocal testing, CI testing, extra release-specific testing
Artifactsgenerating binaries, zip files, github releases etc.
Publishinguploading, pushing, making visible, finalising
Announcingvarious announcement stages and channels

Glossary

Some terminology useful when precision is needed, eg in release scripts.

General

release
A snapshot of the software and related artifacts like executable binaries, which is named, tagged, documented, announced, and usually picked up by packaging systems on various platforms.

version control system, VCS
A tool used for storing and sharing and viewing the history and different lines of development of a software project, or other set of files. hledger uses Git.

repository, repo
A set of files being stored and managed by a VCS. Often published on a repository hosting service, such as Github.

working copy, clone
A local copy of a repository's files. Typically each developer has one or more of these, and can share changes easily with the official public repository.

branch
Some VCS's, including Git, can store multiple branching lines of development within one repository. A working copy can be quickly switched to a different branch to show its content.

master, main
The main branch in a repo, usually named master or main. Pull requests are usually relative to this.

pull request, PR
A request to merge a development branch with master, and any related discussion. On Github, these are kept alongside issues in the issue tracker.

continuous integration, CI
Automated actions that run when new code is pushed to a shared repo, such as running tests or producing binaries. On Github this is called Github Actions and action scripts are called workflows.

hledger-specific

package
A releasable unit of Haskell software. hledger has several core packages usually released together: hledger-lib, hledger, hledger-ui, hledger-web.

hledger version number
A 2-4 part dotted number naming a hledger release or hledger package version: MA.JOR[.MINOR[.FIXUP]] or MA.JOR.99[.PREVIEW] where 99 means "unreleased (MAJOR+1)". See examples below.

hledger version string
A line of text describing a hledger binary, shown by --version. It contains program name, version number, commit hash and date, machine architecture etc. Eg: hledger 1.24.1-g7799d526b-20211210, mac-x86_64

Full release
A release of all four core hledger packages (hledger-lib, hledger, hledger-ui, hledger-web). Major and preview releases are always full releases.

Partial release
A release of just some of the hledger packages. Bugfix and fixup releases are sometimes partial.

Single-version release
A release where all packages have the same version. Major and preview releases are always single-version.

Mixed-version release
A release where the packages have different versions, because of a previous partial release. Bugfix and fixup releases are sometimes mixed-version.

changelog
A CHANGES.md file listing the release history and the changes in each release. There is one for each hledger package and one for the hledger project as a whole.

release notes
The Release Notes page on the hledger website: the combined release history of the core hledger packages, showing user visible changes only.

Releases and builds

Major release
Major releases include new features and incompatible API changes, and normally happen at the start of each quarter's third month (3/1, 6/1, 9/1, 12/1). Example version number: 1.25

Bugfix release
Bugfix releases include only bug fixes, without API changes. These happen when needed, to fix significant bugs in the previous major release. Example version number: 1.25.2 ("second bugfix release for 1.25")

Fixup release
Fixup releases fix packaging errors, with no changes to the hledger software. These should be rare. Example version number: 1.25.0.1 or 1.25.2.1 ("first fixup release for 1.25 / 1.25.2")

Preview release
A preview of the upcoming major release for testers/early adopters, and a test of the release process, published on Github. Not a formal hledger release, eg not published on Hackage, usually not packaged, no bugfix releases, no regression bounties, not shown in release notes. These typically appear in the quarter's first and second month if needed. Example version number: 1.25.99.1 ("preview 1 of 1.26")

CI binaries
Temporary downloadable binaries produced by a run of the linux/mac/windows workflows in the hledger repo. This may happen periodically, eg weekly. Downloading requires a Github login.

Dev build
A local developer build of unreleased code. This is typically in master or a development/PR branch. Example version number: 1.25.99 ("unreleased 1.26-dev")

Repos and branches

hledger repo
The hledger git repository, containing the hledger software, reference manuals, and developer docs. https://github.com/simonmichael/hledger

site repo
The hledger_website git repository, containing most of the hledger website which appears at https://hledger.org. Usually checked out under the hledger repo as site/. https://github.com/simonmichael/hledger_website

master
The branch named master in the hledger repo; the main line of hledger development. Pull requests are usually relative to this.

release branch
Branches named MA.JOR-branch in the hledger repo, eg 1.25-branch. Releases and release previews are always made from a release branch.

Tips

  • Release, or practice releasing, often to improve the process.

  • Use and continually update RELEASING.md. Document procedures and gotchas to save time and enable automation in future.

  • Also the diagram (RELEASING.canvas, made with Obsidian).

  • But don't document prematurely or in too much detail.

  • Make things a little better each time through: simpler, more reliable, better documented, more automated, easier, faster, cheaper, higher quality.

  • When starting a release, save a copy of this file (RELEASING2.md) and update notes there until after release, to avoid obstructing git branch switching.

  • Use and update scripts. See just in the main repo -> RELEASING (and perhaps older stuff in ./Shake.hs and make).

  • Do all releases from a release branch.

  • Update changelogs & announcements in the release branch. master's are updated only by post-release merge. (Related older doc: CHANGELOGS)

  • All release binaries should be built from the release-tagged commit. The binaries' --version output should match the release tag and release date.

  • When releasing a package, also release all the packages that depend on it. Try to do full releases including all the hledger packages, not partial releases.

  • Try to avoid pre-announcing a hard release date. It will always take more time than you think, if you go late you might miss your intended date in many timezones, and there's no point adding unnecessary pressure.

  • The biggest potential time sinks are:

    • reviewing/relearning the process/docs/infrastructure
    • updating/improving the process/docs/infrastructure
    • preparing changelogs
    • building binaries for all platforms
    • troubleshooting github workflow issues
    • followup work due to release mistakes, bugs in new features, or regressions
  • Hard/risky/intensive tasks should be early in the process; during the final countdown, things should be easy.

Release artifacts / value chain

Higher things depend on lower things; when doing a release, work upward from the bottom. (Or downward through the Procedures).

release diagram

Process

Here's an overview of a happy-path hledger release. These steps can be interleaved/reordered a little if needed.

1 Prep software & docs

In main repo, release branch:

  1. Check release readiness
  2. Create/switch to release branch, update versions/dates/docs: just relprep NEW (single-version releases; for mixed-version releases, take more care)
  3. If not the first release in this branch, cherry-pick changes from master: magit l o REL-branch..master (minor releases)
  4. (Could start building/testing/fixing release binaries/workflows/caches here, it takes time: just relbin)
  5. Update install script: hledger-install/hledger-install.sh
  6. Update changelogs: ./Shake changelogs & manually edit (TODO: fix Shake changelogs to not eat whitespace)
  7. Update release notes: doc/relnotes* (TODO: automate release notes, github release notes production; auto-link issue numbers)
  8. Update announcements: doc/ANNOUNCE* (major releases)
  9. Build/test release binaries: just relbin. Troubleshoot/repeat as needed.

In site repo:

  1. Update online manuals: site/Makefile, site/js/site.js, make -C site snapshot-NEW (major releases)
  2. Update install page: site/src/install.md
  3. Update config in hledger.org.caddy (@oldmanpath, @unversionedmanpath, any new redirects)

2 Prep release

In main repo, release branch:

  1. Build final release binaries (just relbin) and tag the release (just reltag) (TODO: don't add the suggested sixth tag yet, it hinders pushing)
  2. Download release binaries (In Safari: don't use the download button; use right-click, Download linked file)
  3. Push release branch & tags to github: git push --tags
  4. Create a github release draft

In main repo, master:

  1. Cherry-pick master-appropriate changes from release branch (including hledger-install): magit l o LASTREL..REL-branch
  2. Commit any process updates: doc/RELEASING.md
  3. Bump version in master (major releases) (TODO: use the sixth tag command suggested above)

3 Release

In main repo, release branch:

  1. Publish on hackage: just hackageupload
  2. Publish github release

In main repo, master:

  1. Push master: just push

In site repo:

  1. Push to github: git push github or magit P p

In hledger.org cloudflare caching settings:

  1. Custom Purge https://hledger.org/js/site.js

On hledger.org VPS:

  1. Restart caddy to enable new redirects
  2. Test https://hledger.org/hledger.html redirect
  3. Test manuals are displaying and highlighting the new version
  4. If needed, make buildall

4 Announce

(major releases, others if needed)

  1. Update hledger entry at https://plaintextaccounting.org/#pta-apps
  2. hledger matrix & irc chats
  3. PTA forum
  4. hledger mail list (& optionally haskell-cafe)
  5. mastodon with #hledger and #plaintextaccounting tags

5 Post-release

  1. Monitor packaging status (stackage, brew, docker, linux, nix etc); keep install page updated
  2. Monitor, follow up on issues, especially regressions; keep doc/REGRESSIONS.md updated

Detailed procedures

Here's more detail of various steps. (These need updating from make/bake/Shake to just.)

LEVEL 1 - DEV

Check dev readiness

LEVEL 2 - TEST

Up-to-date tools

  • Shake.hs uses same resolver, extra deps as stack.yaml, hledger-install.sh
  • Shake binary is up to date (./Shake.hs)
  • commit any changes (message: "tools: shake")

Up-to-date cabal files

  • ./Shake cabalfiles
  • if there are changes, ./Shake cabalfiles -c

Up-to-date help

  • ./Shake cmdhelp
  • if there are changes, ./Shake cmdhelp -c

Up-to-date manuals

  • ./Shake mandates
  • ./Shake manuals
  • if there are changes, ./Shake manuals -c

Up-to-date changelogs

In main repo, master branch:

  • ./Shake changelogs
  • clean up the five CHANGES.mds
  • ./Shake changelogs -c See CHANGELOGS.

Local tests passing

  • make test
  • make doctest
  • make haddocktest

Regular CI tests passing:

  • push to a PR, wait for green
  • or push to simon branch, wait for green at https://ci.hledger.org
  • or tools/push (pushes to simon, then to master)

LEVEL 3 - RELEASE DOCS

Release branch and version number

Create release branch if needed, update all package versions, help, manuals, changelogs (preferred):

  • ./bake prep NEW
    • First ensure hpack --version matches the one in stack --version
    • NEW is MA.JOR[.MINOR|.99.PREVIEWNUM] (eg 1.24.99.1 for 1.25 preview 1)
    • for troubleshooting: bash -x PAUSE=1 ECHO=1 bake ...

Or, bump version of a subset of packages in an existing release branch (not ideal):

  • git switch MA.JOR-branch (magit: b b MA.JOR-branch)
  • ./Shake setversion NEW PKGS -c

Select commits for release

  • cherry pick desirable commits from master (if needed)
    • eg fancy workflow: three magit windows:
      • NEW IN MASTER: l o MAJOR-branch..master, M-x magit-toggle-buffer-lock, M-x toggle-window-dedicated (C-c D)
      • HEAD: regular magit status view
      • RELEASE BRANCH: l o MAJOR-branch, M-x magit-toggle-buffer-lock, M-x toggle-window-dedicated
      • in master window, working from bottom upward, cherry-pick all non-conflicting changes, skipping already-picked/help/manuals/changelog changes

Release changelogs

  • see also CHANGELOGS
  • open all changelogs and release notes in emacs
  • maybe run ./Shake changelogs again
  • manually clean up/finalise changelogs
  • manually add release version/date headings (or fix bake prep)

Release notes

In main repo, update doc/relnotes.md:

  • copy template from top comment
  • replace date and XX
  • add new content from changelogs, excluding hledger-lib
  • add contributors, git shortlog -sn OLD..NEW
  • for a major release, add highlights
  • clean up
  • commit: relnotes: NEW

Github release notes

In main repo, update doc/relnotes.github.md:

  • replace all OLD version strings with NEW
  • copy latest from relnotes.md

Release branch tests passing

  • make test
  • stack exec -- hledger --version, check version, hash, release date, no '+'
  • stack exec -- hledger help | tail, check version, month matches release

LEVEL 4 - RELEASE BINARIES

Multi-platform CI tests passing

  • ./bake bin (push to github/binaries)
  • wait for green on all platforms, resolve failures

Release binaries

With all platform CI tests green on same commit:

  • save native local binaries from that same commit: make install-as-NEW
  • clear out any old zip files/binaries from local Downloads dir
  • in each successful platform job: right click the artifact, Download linked file
  • unpack the github binaries for the local platform

hledger-install script

(major/bugfix/fixup release)

  • update hledger-install/hledger-install.sh
    • HLEDGER_INSTALL_VERSION (release date)
    • hledger official packages (NEW)
    • hledger third-party packages (latest versions on hackage/pypi)
    • RESOLVER and EXTRA_DEPS (same as stack.yaml, or one of them)
  • test ? (won't work until new hledger packages are on hackage) cd; bash ~/src/hledger/hledger-install/hledger-install.sh
  • commit: install: NEW

LEVEL 5 - RELEASED

Pre-release pause

  • stop, afk, take a break
  • review time, energy, availability, decide go/no-go

Pre-release tests passing

Sanity checks:

  • appropriate dates/versions in changelogs and release notes (if late in day, watch for time zone issues)
  • hledger-install script
    • rg '^(HLEDGER(_\w+)?_VERSION|PRICEHIST)' hledger-install/hledger-install.sh
  • binaries' --version output
    • cd ~/Downloads
    • ./hledger --version
    • ./hledger-ui --version
    • ./hledger-web --version
  • binaries' man pages
    • ./hledger --man | tail -1
    • ./hledger-ui --man | tail -1
    • ./hledger-web --man | tail -1

Release tag

  • ensure new version has been set first with Shake or bake
  • ensure no new commits have been made since push to github/binaries
  • in the release branch (?): make tag

Hackage packages

in main repo, release branch:

  • make hackageupload (major/bugfix/fixup release)

Github release draft

After pushing release tags:

Release manuals

(major release)

In site repo:

  • js/site.js: add NEW, 3 places
  • Makefile: add NEW, 3 places
  • commit: manuals: NEW
  • make snapshot-NEW (after ensuring main repo has been release-tagged)
  • push

LEVEL 6 - PUBLISHED

Install page

(major/bugfix/fixup release)

In site repo:

  • update install.md
    • query-replace OLD -> NEW in
      • "current hledger release"
      • CI binaries badges/links, including linux-static-arm32v7 if built
      • "building from source"
      • stack install command
      • cabal install command
    • query-replace OLD-brightgreen -> OLD-red
    • only after release binaries are built (preferably after release is published): update --version outputs (version, hash, date, but not platform)
    • final output line from hledger test (run local build and in terminal for normal speed)
    • Total count from make functest
    • preview
    • commit: install: NEW

LEVEL 7 - ANNOUNCED

Prepare announcements

(major/notable bugfix release)

In release branch, update

  • doc/ANNOUNCE (major release)
    • summary, contributors from release notes
    • any other edits
  • doc/ANNOUNCE.masto
  • commit: ;doc: announce NEW

Announce

(major/bugfix release)

POST RELEASE

Merge release branch changes to master

  • switch back to master
  • check out release branch in another working copy (hledger2)
  • manually merge release changelogs into master changelogs (see also CHANGELOGS)
  • list commits only in release branch: magit l o master..MA.JOR-branch
  • cherry-pick any other useful commits

Bump master to next version

(major release)

  • ./Shake setversion MA.JOR.99 -c
  • ./Shake cmdhelp [-c] (might be empty)
  • ./Shake mandates
  • ./Shake manuals -c

Commit RELEASING.md

  • move copies back to RELEASING.md, RELEASING.canvas
  • re-export RELEASING.png: obsidian > CMD-p > Export as image, don't show logo
  • commit

Push master

in main repo, master branch: push

Post-release followup

  • monitor packaging status, update install page
    • homebrew - expect badge to update soon
    • docker - expect/merge PR
    • nix - expect badge to update after a few days; can check with make nix-hledger-version
    • linux distros - once in a while, follow the links & search for newer versions, update
  • provide support, monitor issues
  • prepare followup release(s) as needed
  • update process docs and tools

Update stackage

  1. update https://github.com/fpco/stackage/blob/master/build-constraints.yaml if needed

  2. monitor for new package versions in nightly: check https://www.stackage.org/package/hledger

Update homebrew formula (old)

  1. ref

    1. helpers: chenrui & stack issue commenters on github, Athas on #haskell

    2. https://docs.brew.sh/How-To-Open-a-Homebrew-Pull-Request

    3. doc

      If a URL is specified, the SHA-256 checksum of the new download should also be specified. A best effort to determine the SHA-256 and formula name will be made if either or both values are not supplied by the user.

      If a tag is specified, the Git commit revision corresponding to that tag must also be specified.

      Note: this command cannot be used to transition a formula from a URL-and-SHA-256 style specification into a tag-and-revision style specifi- cation, nor vice versa. It must use whichever style specification the for- mula already uses.

  2. update homebrew working copy

    1. cd ~/src/DEVTOOLS/homebrew-core

    2. git reset --hard HEAD~1 && git fetch origin && git merge origin/master && git push -f

  3. get release tarball checksums

    export V=X.Y; for P in -lib "" -ui -web; do curl -sO https://hackage.haskell.org/package/hledger$P-$V/hledger$P-$V.tar.gz; done && shasum -a256 *.gz

  4. update Formula/hledger.rb

    1. update tarball urls, shas

    2. update other version references

    3. maybe update stackage snapshot

    4. possible new process, see: https://docs.brew.sh/How-To-Open-a-Homebrew-Pull-Request#create-your-pull-request-from-a-new-branch

    5. test (if possible without too many mac/brew hassles)

      1. brew audit --strict Formula/hledger.rb # some tests are post-install, try also after brew upgrade

      2. brew upgrade Formula/hledger.rb -s -n -v # [scrub download cache, dry run, verbose]

        1. super slow, how to speed up ?

          1. comment out stack update

          2. add , "--stack-root=/Users/simon/.stack" to stack install # fails on permissions

      3. brew test Formula/hledger.rb

    6. commit with name "hledger X.Y" and description "Update to hledger X.Y."

  5. get merged

    1. git push -f simonmichael

    2. gh pr create -f

    3. monitor: PR CI, PR merge, https://formulae.brew.sh/formula/hledger page

    4. ping brew contributors/maintainers if necessary: @chenrui, @carlocab, @SMillerDev

Repositories

Source code and content related to hledger are kept in revision control repositories, mostly using Git.

The official repos:

Other repos exist for third-party hledger tools (hledger-iadd, hledger-interest, etc.) and packages.

ROADMAP

Ideas of where the hledger project should be going next. Being listed here suggests a bit of commitment, perhaps even a schedule. Related: TODO.

2024

Targets:

  • hledger 1.33, april
  • hledger 1.34, june ? TBD
  • hledger 1.35, september ?
  • hledger 1.36, december ?

2023

Targets:

  • hledger 1.32, december
  • hledger 1.31, september
  • hledger 1.30, june
    • demos: built in asciinema demos and maintenance process done, 4 simple demos
    • ghc 9.6 support partly done, hledger-web is blocked on deps
    • process/tools improvement done
    • docs improvement done: 115 doc commits + 112 site commits, manual cleanups & rewrites
  • hledger 1.29, march

Goals:

  • CSV extensibility: workflows to obtain, use, develop, share, contribute ready-to-use CSV rules
  • Scripts extensibility: workflows to obtain, use, develop, share, contribute ready-to-use scripts
  • Interop: clear ledger & beancount import/export how-tos documenting issues & workarounds
  • Better installer: more robust, binary-installing
  • Bar charts: simple built in bar charts
  • Investment: clear updated how-to documenting available tools & best practices for common needs (price fetching, lot reducing, lot reporting, cost reporting, gains reporting)

Priorities:

  • newcomer/learner experience: docs, installers, demos
  • customiser/contributor experience: easy csv rules install/contrib, scripts install/contrib
  • maintainer experience: reduce tech/doc/process/issue debt, increase velocity
  • marketing/community: news updates, mastodon presence
  • interop: solve Ledger/Beancount reading/writing/conversion
  • features: charts, investment

Mission:

  1. Make plain text accounting more usable and useful for all
  2. Bring relief to people experiencing financial and financial technology stress
  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.

2020

Targets:

  • hledger 1.19, september
    • account transactions register, stricter/more correct handling of unbalanced multicommodity transactions (#1177), Track & show deposited lots (#1022), Report unrealized capital gains/losses (#1029)
  • hledger 1.18, june
    • more effective CI setup, updated home page, quickstart, tutorials etc., negative matching in CSV rules,
  • hledger 1.17, march
    • field matching in CSV rules, reduce install hassles with terminfo C lib (?), more import/export options, simple console charts, refreshed home page, faq, tutorials, manuals,

Priorities:

  • Documentation: Improve the docs.
  • Effectiveness: Improve getting-started experience, just-works quality, practicality, real-world usefulness.
  • Investment: Improve suitability for investment tracking (#1015)
  • Charts: Add charts and more visual appeal.
  • Correctness: More support for enforcing correctness & accounting rules.

2019

Targets:

  • hledger 1.16, december
    • ghc 8.8 support, more powerful CSV conversion, updated home page, faq, manuals, reduce install hassles with terminfo C lib

TESTS

About testing in the hledger project, as of 201809.

Kinds of tests

"Here, then, is a list of properties of tests. Not all tests need to exhibit all properties. However, no property should be given up without receiving a property of greater value in return.
  • Isolated — tests should return the same results regardless of the order in which they are run.
  • Composable — if tests are isolated, then I can run 1 or 10 or 100 or 1,000,000 and get the same results.
  • Fast — tests should run quickly.
  • Inspiring — passing the tests should inspire confidence
  • Writable — tests should be cheap to write relative to the cost of the code being tested.
  • Readable — tests should be comprehensible for reader, invoking the motivation for writing this particular test.
  • Behavioral — tests should be sensitive to changes in the behavior of the code under test. If the behavior changes, the test result should change.
  • Structure-insensitive — tests should not change their result if the structure of the code changes.
  • Automated — tests should run without human intervention.
  • Specific — if a test fails, the cause of the failure should be obvious.
  • Deterministic — if nothing changes, the test result shouldn’t change.
  • Predictive — if the tests all pass, then the code under test should be suitable for production." --Kent Beck
  1. Unit tests

    Unit tests exercise small chunks of functionality. In hledger, that means a function. So, many of our functions have one or more unit tests. These are mostly in hledger-lib, with a few in hledger.

    Our unit tests use the tasty test runner, tasty-hunit HUnit-style tests, and some helpers from Hledger.Utils.Test, such as:

    • tests and test aliases for testGroup and testCase
    • assert* helpers for constructing various kinds of assertions

    We would like our unit tests to be:

    • easy to read (clear, concise)
    • easy to write (low boilerplate, low cognitive load)
    • easy to maintain (easy to edit, easy to refactor, robust)
    • easy to associate with the code under test (easy to view/jump between code & test, easy to estimate coverage)
    • and scalable (usable for all devs, easy to run and select, suitable for small/large modules/packages).

    Here's the current pattern (let us know if you see a better way):

    module Foo (
      ...
      tests_Foo -- export this module's and submodules' tests
    )
    where
    import Hledger  -- provides Hledger.Utils.Test helpers
    import Bar      -- submodules, providing tests_Bar etc.
    import Baz
    
    functionA = ...
    functionB = ...
    functionC = ...
    functionD = ...
    
    tests_Foo = tests "Foo" [ -- define tests at the end of each module
    
       -- a group of several named tests for functionA
       tests "functionA" [
         test "a basic test"           $ assertBool "" SOMEBOOL
        ,test "a pretty equality test" $ SOMEEXPR @?= EXPECTEDVALUE
        ,test "a pretty parsing test"  $ assertParseEq PARSER INPUT EXPECTEDRESULT
        ,test "a multiple assertions test" $ do
          A @?= B
          doSomeIO
          C @?= D
        ]
    
       -- a single test containing multiple unnamed assertions for functionB
      ,test "functionB" $ do
         assertBool "" BOOL
         EXPR @?= VALUE
    
      ,tests_Foo            -- aggregate submodule tests
      ,tests_Bar
      ]
    

    Here are some real-world examples.

    The unit tests are shipped as part of the hledger executable, and can always be run via the test command (hledger test).

  2. Doc tests

    Like unit tests, but defined inside functions' haddock documentation, in the style of a GHCI transcript. These test functionality, provide usage examples in the API docs, and test those examples, all at once. They are a bit more finicky and slower than unit tests. See doctest for more.

    doctests do not work on Mac with GHC 8.4+, out of the box. See ghc#15105 for current status and a workaround.

  3. Functional tests

    Functional tests test the overall functioning of the program. For hledger, that means running hledger with various inputs and options and checking for the expected output. This exercises functionality in the hledger and hledger-lib packages. We do this with shelltestrunner. Tests are defined in files named *.test under hledger/test/, grouped by component (command or topic name). For more about these, see the README there.

  4. Code tests

    We have some tests aimed at testing eg code quality, generally runnable via just. Eg just haddocktest, just hlinttest.

  5. Package test suites

    Haskell tools like stack and cabal recognise test suites defined in a package's cabal file (or package.yaml file). These can be run via stack test, cabal test etc., and they are required to build and pass by services like Stackage. Here are the currently hledger package test suites:


    package test suite what it runs hledger-lib doctests doctests hledger-lib easytests unit tests hledger test builtin test command (hledger's + hledger-lib's unit tests) hledger-ui
    hledger-web


Coverage

This means how thoroughly the code is tested - both in breadth (are all parts of the code tested at least a little ?) and in depth (are all possible code paths, states, situations tested ?).

Our current test coverage can be summarised like so:


package unit doc functional hledger-lib X X X hledger X X hledger-ui
hledger-web


There are ways to generate detailed coverage reports for haskell unit tests, at least. It would be useful to set this up for hledger.

How to run tests

Run unit tests:

$ just unittest

Run doctests:

$ just doctest

Run functional tests (and unit tests, now):

$ stack install shelltestrunner
$ just functest

Run the package tests (unit tests, maybe doctests, but not functional tests) of all or selected packages.

$ stack test [PKG]

Run "default tests: package plus functional tests":

$ just test

Test generation of haddock docs:

$ just haddocktest

Run built-in hledger/hledger-lib unit tests via hledger command:

$ hledger test  # test installed hledger
$ stack build hledger && stack exec -- hledger test  # test just-built hledger
$ hledger test --help
test [TESTPATTERN] [SEED]
  Run the unit tests built in to hledger-lib and hledger,
  printing results on stdout and exiting with success or failure.
  Tests are run in two batches: easytest-based and hunit-based tests.
  If any test fails or gives an error, the exit code will be non-zero.
  If a pattern argument (case sensitive) is provided, only easytests
  in that scope and only hunit tests whose name contains it are run.
  If a numeric second argument is provided, it will set the randomness
  seed for easytests.

Rebuild and rerun hledger/hledger-lib doc tests via ghcid:

$ just ghcid-doctest

See all test-related just rules:

$ just h test

Version numbers

Some places version numbers appear:

  • --version (and sometimes --help) output of all hledger* executables
  • web manuals on hledger.org
  • download page
  • changelogs
  • release notes
  • release announcements
  • hackage/stackage uris
  • cabal tarball filenames
  • platform-specific packages

Some old version numbering goals:

  1. automation, robustness, simplicity, platform independence
  2. cabal versions must be all-numeric
  3. release versions can be concise (without extra .0's)
  4. releases should have a corresponding VCS tag
  5. development builds should have a precise version appearing in --version
  6. development builds should generate cabal packages with non-confusing versions
  7. there should be a way to mark builds/releases as alpha or beta
  8. avoid unnecessary compiling and linking
  9. minimise VCS noise and syncing issues (commits, unrecorded changes)

Current version numbering policy:

  • We (should) follow https://haskell.org/haskellwiki/Package_versioning_policy

  • The "full release version" is ma.jor.minor, where minor is 0 for a normal release or 1..n for bugfix releases. Each component is a natural number (can be >= 10). Eg: 1.13 major release, 1.13.1 bugfix release.

  • The "release version", which we prefer to use when possible, is just ma.jor when minor is 0. Ie elide the dot zero.

  • The build version is ma.jor.minor+patches, where patches is the number of patches applied in the current repo since the last release tag.

  • hledger --version shows the release version or build version as appropriate.

  • Release tags in the VCS are like PKG-VERSION. Eg hledger-1.13,

  • hledger-ui-1.13.1.

Current process:

  • In each hledger package directory there's a .version file containing its desired version number.

  • After changing a .version file: run ./Shake setversion to propagate the versions to all other places in the packages where they should appear. This is not perfect (see Shake.hs) so review and manually adjust the proposed changes before committing. Those places include (you can also run these rules individually):

    • PKG/package.yaml contains the cabal package version declaration, bounds on other hledger packages, and a CPP VERSION macro used in hledger/Hledger/Cli/Version.hs. Changes in package.yaml will be propagated to PKG/PKG.cabal on the next stack build or --dry-run build or with just cabalfiles.

    • PKG/.version.m4 contains the version macro used in documentation source files (*.m4.md). It is updated by ./Shake setversion.

    • PKG/.date.m4 contains the monthyear macro used in man pages. It is updated by ./Shake manuals.

  • At release time:

    • ./Shake PKG/CHANGES.md-finalise converts the topmost heading, if it is an interim heading (just a commit hash), to a permanent heading containing the version and today's date.

    • for each package being released, a PKG-VERSION git tag is created.

  • At major release time:

    • A new snapshot of the reference docs is added to the website, by ./Shake site/doc/VERSION/.snapshot, and added to the links in site/js/site.js.