
Hledger: Robust Plain Text Accounting - lwhsiao
https://hledger.org/
======
smichael
I'm hledger's lead developer, thanks for the mention. If you have any
questions or hit any snags trying it out, I'm ready to help. Critique of
product, docs etc. is welcome. Last time (2017) we got 2 out of 41 comments
about hledger, so I'm hoping for a few more. :) [https://hledger.org/release-
notes](https://hledger.org/release-notes) has the (software) changes since
then. Our chat is #hledger on Freenode:
[http://webchat.freenode.net/?channels=hledger](http://webchat.freenode.net/?channels=hledger)

~~~
doomrobo
Are there any iOS apps that interop with hledger? I've been using iQIF [0] for
a bit, but it only exports things in QIF format.

[0] [http://www.iqif.info/iQIF/](http://www.iqif.info/iQIF/)

~~~
dragonsh
Use this
[https://github.com/giacomos/qifparse](https://github.com/giacomos/qifparse)
combined with
[https://github.com/quentinsf/icsv2ledger](https://github.com/quentinsf/icsv2ledger)
to automatically generate journal entried in ledger-cli format which can be
used by hledger directly.

You can also fork and modify it to generate hledger format files directly.

~~~
vitoc
Check out the parser extension approach at
[https://Prudent.me](https://Prudent.me)

------
stevesimmons
These ledger formats have what feels to me an overly verbose multi-line
structure for each transaction.

For 20 years, I have been using a format that combines a diary and ledger. The
'to' and 'from' accounts are either bank/asset accounts (sav, cc, hl for home
loan, ...) or spend categories (snack, shopping, clothes, ... ). Each year I
finish 31 December with a `# Closing balances` section for each
bank/investment/pension account, then start a new text file `2019.txt` with an
`# Opening balances` section before 1 January's entry.

Here is a typical day's format:

    
    
      # 2019-05-27 Mon
      2.50    sav>lunch    Cafe Nero - Coffee
      100.00  sav>cc       Transfer - Credit card repayment
      amt     from>to      forex: shop - desc [txn #] 
      # Diary
      * <topic> - <what>
      * Times - Sleep 01:00-06:00 5h. Run 45m. Work 08:00-18:00 10h.
      * Work - Meeting with xxx on xxx. We agreed to xxx.
      * Friends - Lunch with xxx
      * Note - Running shoes are Asics GT-2000, size 48, model T500N.
      # Links
      * Hledger: Robust plain text accounting - https://news.ycombinator.com/item?id=20012499
    

For the first 5 years, I was single and reconciled everything down to the
cent, using an AWK script to generate totals to match against my bank
statements.

When I got married, all our accounts switched to joint ones. I didn't want to
track my my partner's side of our spending. So these logs became more a
personal diary than a fully reconciled ledger. Anything I want to remember
long-term goes either there or secured in a password safe.

~~~
kd5bjo
You reinvented the accounting daybook: Shops doing paper accounting never went
through the hassle of double-entry accounts for each sale. Instead, each was a
single line in the day’s journal, and closing for the day involved reconciling
the till with the sales journal, and entering the entire day’s revenue as a
single transaction in the master account book.

------
meruru
See also [https://plaintextaccounting.org](https://plaintextaccounting.org)
for more on the broader ledger ecosystem.

~~~
wodenokoto
Which is also a really good introduction to accounting. I based my spreadsheet
accounting on this guide.

~~~
smichael
wodenokoto, may I ask which guide ?

------
AdamGibbins
Personally I prefer beancount, incredibly pluggable in python (imo a
friendlier language to haskell), additionally much stricter in multiple
aspects. Been maintaining my ledger with ledger-cli and then beacount for 10
years now.

~~~
dragonsh
I am a user of both ledger-cli with emacs lisp bindings and beancount, fava
with bank PDF statement preprocessing for journal entries. Used statistical
inference to generate journal entries automatcomatically with tools like
reckon (now use another one, as it's not maintained) and self written
algorithms in Python with sci-kit learn. Works very well so far for over 6
years.

~~~
nsomaru
I’d love to see your scripts if you could share them? Especially the part to
automatically generate entries

~~~
dragonsh
I have built the scripts using
[https://github.com/quentinsf/icsv2ledger/blob/master/icsv2le...](https://github.com/quentinsf/icsv2ledger/blob/master/icsv2ledger.py)
as base.

You can do the same as the scripts I wrote are specific to banks I am using
and the pdf files they send, except one of the banks which provide qif format
file which I convert to CSV. You can look at
[https://github.com/beancount/beancount/tree/master/examples/...](https://github.com/beancount/beancount/tree/master/examples/ingest)
for beancount custom import example.

------
edwintorok
I've recently come across two systems that use hledger for processing bank
statements: [https://pauley.org.za/hledger-
flow/](https://pauley.org.za/hledger-flow/) [https://github.com/adept/full-
fledged-hledger/wiki](https://github.com/adept/full-fledged-hledger/wiki)

They treat the statements as the single source of truth, with preprocessing
and rules that can be used to turn them into hledger journals.

~~~
apauley
I'd be happy to hear if you've found hledger-flow useful. Feel free to create
an issue on [https://github.com/apauley/hledger-
flow](https://github.com/apauley/hledger-flow) if you think anything can be
improved.

------
enriquto
I would appreciate a small section on the README file that explains how to
compile the program, what are the requirements (e.g., what debian packages to
install), and a minimal cli use case.

Also, running "make" prints a daunting 96 lines of text of which the last 50
(the ones that you get to see) are mostly useless. If I run "make build" it
starts to download stuff from the internet, which I find extremely impolite,
as I already downloaded the source code. What is the point of not including
all the neeedded source code in the source distribution?

~~~
TomK32
Haskell is a bit rare and so are its libraries rarely installed on systems. I
can't remember, as arch has a proper package and I built hledger only once on
a different system, but those downloads might be just libraries.

~~~
enriquto
Whatever. Either they are dependencies that are required to be installed
beforehand (just like a haskel compiler is required), or they are included in
the source code distribution. Downloading them at compile time is very
strange, and in my eyes unacceptable.

~~~
owaty
It's not different from what pip install or gem install do by default.

A mature Haskell project such as hledger has probably a few hundred of
transitive dependencies, so you won't be able to install them individually by
hand. You might try to figure whether each of them is packaged by your
distribution (unlikely, unless hledger itself is packaged), but that's not a
typical path — just don't use make in that case.

~~~
enriquto
> It's not different from what pip install or gem install do by default.

Pip installing required packages is alright. But I expect setup.py to NOT call
pip install by itself.

> A mature Haskell project such as hledger has probably a few hundred of
> transitive dependencies

That's horrifying!

~~~
smichael
Maybe so. :) It's also code reuse, actually working, and quite common for non-
trivial end-user apps, particularly in the Haskell world. I'm not sure what
kind of software you're used to. It's interesting to imagine how hledger would
be different if I wrote everything myself, but I have to think it would be far
less further along.

~~~
enriquto
Thanks for your answer! I'm used to mostly C, C++ and Python projects. In the
worst cases, when I download and compile a program I have to install two or
three dependencies, and it is OK if I do it by hand. For an eminently unix-
friendly program like hledger I was expecting a similar ease of installation
(i.e., I have an arbitrary haskell compiler and I can just compile the program
running "make"). But I was surprised that this is not the case; obviously the
traditions of the haskell community are different. Yet, it seems very
difficult to install this program in a controlled environment without internet
access.

~~~
smichael
Thanks. Well if we assume "arbitrary haskell compiler" means GHC and stack (or
just stack), "make install" will compile it. But yes, there will be much
downloading, unless you just happen to have all those haskell libs cached
locally from previous builds.

"stack install hledger" would compile just the command line tool (not the
curses & web tools), so less downloading.

For end-users who want minimum downloading/building, we recommend one of the
binary packages on the download page (nix, docker, homebrew, windows/wine),
but that's not you I think.

------
sctb
Discussion from 2017:
[https://news.ycombinator.com/item?id=15818682](https://news.ycombinator.com/item?id=15818682).

------
tangue
If you're using Emacs and are not familiar with double-entry accounting this
video is a good introduction :
[https://www.youtube.com/watch?v=cjoCNRpLanY](https://www.youtube.com/watch?v=cjoCNRpLanY)
(it's based on ledger not hledger)

------
Nerada
Does Hledger support encrypting the ledger file? I don't mean placing the
ledger in an encrypted container and mounting/unmounting as necessary, but I
guess 'natively' as part of the file (think an encrypted Excel document).

I'm a GnuCash user, but my biggest gripe is not being able to encrypt my
accounting file. I'd love to have it sync across my devices via x cloud
platform, but I'm always paranoid about uploading anything that isn't password
protected. If I was able to do that it'd be the push that gets me to switch
from the GnuCash GUI to a plaintext account.

Maybe there's some other way I haven't thought of without a high barrier to
entry like constantly mounting and unmounting an encrypted container to make
changes.

~~~
AdamGibbins
beancount does, you can transparently encrypt your data.

~~~
dhruvkar
is there any documentation for this?

------
HorizonXP
I just spent the last week reconciling my books, using my scores of Excel
files as a source, moving them to ledger.

I learned rudimentary double-entry accounting in high school, and I love that
ledger enforces it so that I can be strict about my tracking. I still have to
figure out how to produce balance sheets and income statements so that I can
confirm that my accounts are balanced correctly. Honestly wish I had done this
sooner.

Edit: Looking at beancount again, I should just use that instead of ledger.
Ledger's simple, elegant, and nice, but I'm a Python guy, and I should just
use what I know.

------
accrual
I've used the original ledger [0] for a few years to track my day-to-day cash
flow and appreciate the simplicity. I create a new file every year and manage
it with standard Unix tools, plus a few shell and Python wrappers to make
entries and reports a little easier.

The hardest part is just getting started and getting into the habit of
maintaining the ledger. Once you start tracking it's difficult to stop! Seeing
all of your accounts line up is a reward in itself.

[0] [https://www.ledger-cli.org](https://www.ledger-cli.org)

------
fiatjaf
See also [http://hledger.alhur.es/](http://hledger.alhur.es/), hledger
compiled for the web in a playground (you can also save your stuff to
remoteStorage).

Source: [https://github.com/fiatjaf/hledger-
web](https://github.com/fiatjaf/hledger-web)

------
techntoke
hledger looks pretty amazing, but there are so many dependencies and updates
with Haskell programs. Almost every other day I'm updating like 20 Haskell
packages for hledger or pandoc when I run a system update. It isn't inherently
bad, but never had this issue with a C or Go program.

~~~
smichael
May I ask what system is that ?

Maybe one of the other packaging methods will give you a more stable
installation ? [https://hledger.org/download](https://hledger.org/download)

~~~
jeremyjh
I'd bet money its Arch. I love `pacman` but the package spam for Haskell is a
bit much as its automatically updating everytime a new package is hosted on
hackage. I'd recommend skipping it all together, just use stack and build it
from source, let stack manage GHC and you'll never update except when you want
to.

~~~
Shoop
To add some context to this, the reason why packages which depend on haskell
in arch are such a mess is because "the arch way" is to dynamically link
everything. This has its advantages (namely security), but is not super
compatible with the haskell ecosystem which is designed with static linking in
mind.

Like my parent commenter, I recommend just doing everything through stack.
stack-static from the AUR [0] is the convenient way to do this (rather than
dragging in a few gigs of ghc dependencies for stack only to have stack create
its own local ghc installation as well).

[0] [https://aur.archlinux.org/packages/stack-
static/](https://aur.archlinux.org/packages/stack-static/)

~~~
bscphil
If I'm not mistaken, Arch has some ornery maintainers in the Haskell
department as well. Pandoc in particular _used_ to have a static package, but
now it can't be installed without pulling in a bunch of Haskell development
packages. The maintainer in this case refused to fix the problem and closes
all bug reports without comment.

Fortunately there's [https://aur.archlinux.org/packages/pandoc-
bin/](https://aur.archlinux.org/packages/pandoc-bin/)

------
ryanobjc
Hledger is pretty good for basic accounting. I recently had to do some
tracking of stocks and lots and forex and the developers decided not to
implement lot-based tracking. Which made it impossible to accurately track
money.

So back to plain ledger. If I was starting over I'd probably consider
beancount. But there's only so much I can do a day.

~~~
smichael
I guess you haven’t seen
[https://github.com/simonmichael/hledger/issues/1015](https://github.com/simonmichael/hledger/issues/1015)
?

~~~
ryanobjc
I have but I couldn't make it work. The issue seemed to boil down to "when do
you price commodities?". Hledger doesn't let you report the price at the
actual sale. The history flag didn't work I forget the reasons at this moment
but if I recall correctly it boiled down to "price at market" type of
features.

That works fine for asset type accounts. But when we are taking about as-was
accounting it doesn't work well enough. Beancount has some interesting ideas
about lot tracking but switching cost is too high - I have years and 35k
lines.

~~~
smichael
What I mean is:

\- "The developers decided not to implement lot-based tracking" seems a
misunderstanding. Perhaps "the developers have dragged their heels on exactly
replicating Ledger's handling of lots" is more accurate (and I could justify
that!).

\- we have never had sophisticated valuation/investment features, but there's
a bunch of work in progress on this area right now; more flexible valuation
([https://github.com/simonmichael/hledger/issues/329](https://github.com/simonmichael/hledger/issues/329))
is a current focus and needs testers..

And, right now it should be possible to manage lots manually, similar to
[http://rantsideasstuff.com/posts/2018/07/01-tracking-
investm...](http://rantsideasstuff.com/posts/2018/07/01-tracking-investments-
in-lots-with-hledger) . It's a manual process in Ledger too after all. Let's
chat in #hledger if you'd like.

------
z3t4
A ledger should be a database with only select and insert privileges. eg. No
update or delete queries should be allowed.

~~~
dmos62
That's a solved problem: git.

~~~
tty2300
Git allows rewriting history though.

~~~
jolmg
If you're sharing the repo, then you'll have to convince everyone to discard
their copy of the original history and accept your modified version. And if
that's done and everyone accepts the new history, the original can still be
kept in each copy of the repo on another branch and the rewrite seen in the
reflog. If commits are being signed, you can keep undeniable record of who
contributed what in the original history, and who rewrote it.

If the point is to forbid you to modify even when you're the only person
working on it, any common database will allow you to update and delete when
you enter with administrative privileges, so I don't see the difference.

------
noufalibrahim
I used to be a heavy ledger-cli user and wrote about my setup here
[http://nibrahim.net.in/2015/11/07/ledger_and_personal_financ...](http://nibrahim.net.in/2015/11/07/ledger_and_personal_finance.html)

~~~
malicz
I am a freelance programmer and I use hledger in combination with beancount
for a couple of years. why did you change and to what?

------
mongol
I looked into plain text accounting but Swedish bookkeeping/accounting
practises forbid practises where past records can be modified. It needs to be
append-only. I started developing a command line accounting tool using SQLite
and Go but it is only half-finished.

~~~
estsauver
Git + Open Timestamps maybe?

~~~
magnamerc
Or maybe _gasp_ a blockchain.

------
billfruit
Does it do 'envelope' style budgets like YNAB?

~~~
smichael
You can do those with it, in a manual way (I did for a year or two). More info
on this can be found at
[https://plaintextaccounting.org/#budgeting](https://plaintextaccounting.org/#budgeting).

There's also non-envelope style budget reports built in. For more on these see
[https://hledger.org/manual.html#budget-
report](https://hledger.org/manual.html#budget-report) and
[https://hledger.org/Budgeting-and-
forecasting.html](https://hledger.org/Budgeting-and-forecasting.html).

------
kazinator
This is exactly how I do my accounting: record transactions in a plain text
file, which is processed by the system, resulting in various ledger and
account type objects being in a certain state, which can then be subject to
queries and reports.

Mine is written in TXR Lisp. It is not released to the public.

All input is recorded via function call expressions, which are of course
S-exps. There is only one single macro in the whole thing: _def-date-var_
defines a variable that exhibits different values depending on a effective
date context (established by a dynamically scoped date variable). With this we
can do things like different tax rates for different periods, without having
to introduce such things as parameters appearing in every input item.

> _The amounts within a transaction must always sum up to zero. As a
> convenience, one amount may be left blank; it will be inferred so as to
> balance the transaction._

I also have transactions that sum to zero. There can be three or more account
deltas in a transaction, not only two.

This is not how accounting is taught in North America, though; I came up with
it myself.

Inferring one amount is a bad idea. My system throws an exception if the
transaction amounts do not add to zero.

However, the entry of items is done via various convenience functions, most of
which generate the necessary transactions against all the right accounts. For
instance, recording a new asset:

    
    
      (record-asset 1 "Discombobulator" :cca-50 ;; ID, description, capital-cost-allowance class
                    (date 2017 1 23) ($ 79.99) ($ 4.00) ($ 5.60)) ;; base price, GST, PST taxes.
    

From the looks of the journal format of hledger, it's too generic. I wouldn't
want to use anything of the sort.

Different items need their own syntax. I don't want to record a simple
purchase of a recurring expense in the same way that I record an invoice.

I bind a new variable when recording an invoice:

    
    
      (defparml %inv-0042%
        (new invoice
             number 42
             provider %my-addr%
             client %foo-client-addr%
             remit-to "Kazinator"
             remit-days 25
             date (date 2017 6 30)
             items (list (new time-unit
                              start (date 2017 6 1)
                              end (date 2017 6 2)
                              hours 16
                              rate %rate-foo-client-2017%)
                         (new time-unit
                              start (date 2017 6 5)
                              end (date 2017 6 9)
                              hours 40
                              rate %rate-foo-client-2017%)
                         (new time-unit
                              start (date 2017 6 12)
                              end (date 2017 6 16)
                              hours 40
                              rate %rate-foo-client-2017%)
                         (new time-unit
                              start (date 2017 6 19)
                              end (date 2017 6 23)
                              hours 40
                              rate %rate-foo-client-2017%)
                         (new time-unit
                              start (date 2017 6 26)
                              end (date 2017 6 30)
                              hours 40
                              rate %rate-foo-client-2017%))))
    

There is no custom DSL here at all; just the _new_ macro of the object system.

This is then recorded using a plain function call to the convenience function
_record-invoice_ :

    
    
      (record-invoice %inv-0042% "Invoice 0042 issued to Foo, Inc.")
    

This function will digest the invoice and post the right amounts into various
accounts. The GST tax has to be charged, and such,

When the invoice is paid:

    
    
      (record-paid-invoice %inv-0042% "Invoice 0042 paid by Foo, Inc."
                           (date 2017 7 31))
    

this function will also update multiple accounts, including the automatic
withholding of income tax, based on the withholding rate configured for the
date into which the invoice lands.

To generate a nice HTML invoice, at the REPL, I just call the .(html ...)
method of the invoice object, which takes a stream argument.

    
    
      1> (with-stream (s (open-file "invoice.html" "w"))
           %inv-0042%.(html s))

