1.99.3
Pre-release
Release notes
(2.0 preview 3)
conventional file layout for rules/data/prices,
get command,
commodity aliases,
print aligns by decimal mark,
more intuitive precision behaviour,
more robust lot detection,
average cost basis methods,
customisable report titles,
prices --summary report,
more robust roi report.
hledger 1.99.3
Breaking changes
-
CLI:
--verbose-tagsis no longer a general flag, it is now a
command-specific flag forprintandrewrite. -
Commodities & prices:
commodities --usednow only shows
commodities used in transactions; use--pricedto see commodities
used in P directives. -
Data import: The CSV
sourceandarchiverules now read from/write to a
journal-adjacentdata/directory by default. -
Journal: Inferred missing amounts no longer affect display precisions or
entry balancing precisions. -
Journal: account names whose final part is enclosed in curly braces
(likeassets:{foo}) are expected to have a valid lot subaccount
name within the braces, and will raise an error if not. This is now
documented. Also this error checking (and the hiding by default) of
explicit lot subaccounts can now be disabled with-I/--ignore-lots. -
print:
printnow aligns posting amounts by decimal mark, by default.
--layout=hledger1restores the old layout.
Command line
-
We now show a clear error when no value is provided for a
value-requiring option. (Eg,hledger import -f --dry-runno
longer tries to use--dry-runas-f's value.) -
Bad option values are now reliably reported as an error.
Previously, some invalid option values triggered an error only if
the option was actually used by the command being run. -
On Windows, file paths containing spaces are now quoted properly
when invoking external helpers (info, man, tldr, pager,$EDITOR,
hledger-iadd). This affected hledger-ui'sAandEkeys, and
hledger's help/pager invocations.
#2646
Commodities & prices
-
Commodity aliases:
In journal files, commodity directives can now define one or more
aliases for the commodity. Eg here USD has three aliases:commodity USD 1.00 ; alias: $ US$ "us dollars"A 1:1 market price is inferred for these, so you can use
-Xto
freely convert between them. This is useful eg if your journal and
your downloaded market price data use different symbols for a currency.cur:COMMqueries now match COMM or any of its commodity aliases.
To match only a specific symbol without considering aliases, usesym:SYM. -
commodities:--usednow only shows commodities used in transactions;
a new--pricedflag shows commodities used in P directives.
Separating these makes--usedmore useful with a date query. -
commoditiesnow supportsdate:queries (and/or-b/-e/-p
report period options), affecting the--usedand--pricedreports. -
priceshas a new--summarymode. -
priceshas a new--locationsflag, showing the file and line number
of each price's directive or transaction from which it was inferred.
Data entry
-
addandimportnow test for faulty filesystems before writing.
Some filesystems, eg an Android shared filesystem in Termux, overwrite when you tell them to append.
hledger now tests for this before writing to the journal, to reduce risk of data loss.
#2577 -
addnow offers useful default amounts after entering a balance assignment.
Previously, amounts entered via= BALANCEAMOUNTwere not affecting
subsequent postings' default amounts, making it difficult to
complete data entry. Now, appropriate balancing amounts are offered. -
addno longer restarts at posting 1 if the final transaction checks fail.
So if the entered transaction fails to balance or satisfy balance assertions,
it now just reprompts for another posting, instead of discarding the postings
entered. (This is most visible when entering balance assignments.) -
add's default amounts are now displayed with the journal's commodity
display styles, helping to avoid misparsing of decimal marks. #2645
Data import
-
getis a new command for fetching transaction data and market prices.
It runs two helper scripts which you can customise:data/getdatato gather transaction data (eg CSV) files indata/prices/getpricesto download market prices, to be saved inprices/
Or with
--transactionsor--prices, only the selected phase is run.
The data and prices directories are autocreated if needed, next to the main journal file.
Sample scripts can be found in https://github.com/simonmichael/hledger/tree/main/bin.
The samplegetpricesrequirespricehist. -
importwith no file arguments now reads from all.rulesfiles in
therules/directory next to the main journal file, by default.
Files whose name begins with.or_are skipped (this is useful
for included files which should not be read directly.) -
import -g/--getruns thegetcommand before importing. So
with appropriate helper scripts, a complete import workflow can be:
hledger import -g [--dry-run] -
importnow archives only when new transactions were actually
imported. This avoids creating duplicate archives, eg with a
data-generatingsourcerule likesource | paypalcsv .... -
The CSV
sourceandarchiverules now read from/write to the
journal'sdata/directory by default:sourcelooks for bare filenames/relative paths first indata/,
then in~/Downloads.
(Except paths beginning with./or../- these are relative
to the rules file, as before.)archivesaves todata/archive/, autocreating that if needed.
(A breaking change; previously it saved in a data dir next to the rules file.)
-
On Windows,
sourcefile paths with a drive letter likeC:\foo
are now properly recognised as absolute paths (not relative). -
A
#character in asourcerule now always starts a same-line
comment, even if it appears after|.
Error messages
-
Balance assertion errors: in the suggested troubleshooting command,
commodity symbols containing regex metacharacters (like $) are now
properly quoted for the shell. -
In error messages which compare two amounts (balance assertions,
recorded gain,check basis), when the rounded amounts look
identical, we now show more decimal places to make the difference
visible. #2636 -
Certain errors when reading a CSV or rules file
(eg a missing date rule, or encoding/skip/timezone errors)
now show the path of the problem rules file. -
import's "no data files" error message is improved. -
With an unrecognised command argument, the error message now shows
the bad argument, and the config file path if it came from a config
file. #2489
Journal
-
Inferred missing amounts no longer affect display precisions or the
entries' local balancing precisions. So a high-precision inferred
amount won't affect the number of decimals displayed in reports, or
the precision required to balance the entry. So,- An unseen high-precision balancing amount, eg inferred from a
precise@price, no longer makes it harder to balance the entry. - Some reports that used to add unnecessary decimal zeros no longer do so.
- Reports more consistently show amounts smaller than their
commodity's display precision as0.
- An unseen high-precision balancing amount, eg inferred from a
Lots & gains
-
In command line help,
--lotshas moved from "General input flags"
to "General output flags". -
hledger can now detect gain postings heuristically, without
requiring that their account is declared astype:G. Five styles
for writing disposal transactions are documented in the manual's
"Recording disposals" section. -
The
G/GainandU/UnrealisedGainaccount types are now
inferred from conventional English names, like the other types. Eg
revenues:gain,income:capital-gains,equity:unrealised-gain,
equity:unrealized gainsare recognised as type G, G, U, U. -
Gain amounts are now inferred or checked at the gain commodity's
local precision within the entry. This makes it easier to read
inferred gain amounts and to write explicit gain amounts.
As a special case, if the local precision is zero but the gain is a
non-integer, 2 decimal places are assumed.
Inferred gain amounts preserve their full precision internally, so
they can be viewed more precisely with a command likeprint -c '$1.000000' --round=soft. -
Lot transfer destinations are now checked more carefully: any
specified lot details on the destination annotation (date, label,
cost) must match the source lot, or an error is raised. (Previously,
this would silently disrupt lot identities.) -
The
AVERAGE/AVERAGEALLcost basis methods now properly
recalculate the pool-wide average cost after each acquisition.
And when they are used, lot subaccount names now omit the cost
({2026-01-15}rather than{2026-01-15, $50}), so account names
remain stable as new lots are acquired.
#2581 -
check basisno longer fails because of non-terminating decimals in
the unit cost (eg$50/7 = $7.142857...).
#2636 -
Harmless commodity style differences in explicit lot subaccount
names are now ignored (eg$60vs$ 60). -
Explicit lot subaccounts are detected more robustly, tolerating
colons or curly braces within the label or commodity symbol. -
Explicit lot subaccounts now require a comma after the date.
-
The
--ignore-lots/-Iflags disable explicit lot subaccounts
detection and error checking. [#2649] -
When disposing a single lot with a balance assertion, we no longer
generate an unnecessary new assertion posting. -
If a Ledger-style
(LOTNOTE)annotation contains double quotes,
these are now stripped so they don't clash with hledger's cost basis
syntax and break round-trip parsing. -
When generating labels for unlabelled lots acquired on the same day,
we now pick the smallest numeric label not already used on that
date, avoiding collisions with user-provided labels. -
When there's only one unlabelled acquisition on a date, we no longer
generate an unnecessary label.
print & print-like commands
-
--verbose-tagsis now a command-specific flag forprintand
rewrite(not a general flag). -
print's-xand--lotsflags are decoupled again (-xno
longer implies--lots). -
printhas a new-a/--allconvenience flag that shows all
details useful for troubleshooting: it turns on--explicit,
--lots, and--verbose-tags. -
printnow properly shows inferred per-lot dispose/transfer
postings only when in--lotsmode. -
print --output-format=beancountnow also encodes non-ASCII letters
in tag names (metadata keys), ensuring Beancount can read them.
#2576 -
printnow aligns posting amounts at their decimal marks by
default. The target column is 53; this is increased when needed to
preserve 2+ spaces between accounts and amounts.Also, the
=,==,=*,==*operators of balance assertions and
assignments are aligned in a column one space past the transaction's
rightmost posting amount. And the assertion/assignment amounts are
themselves decimal-aligned, with the leftmost one starting one space
past the widest operator.A new
--layoutoption (also supported byadd,close,import,
rewrite) customises this:--layout=COL(a positive integer) sets a different target column.--layout=hledger1revert's to hledger 1's layout (each
transaction's amounts right-aligned within an expandable 12-char
region).
Reports
-
A new
--title=TITLEoption sets or customises a title heading for
most reports, when producingtxt,htmlorfodsoutput (or
csvortsvoutput, in some cases).balancesheet,
balancesheetequity,cashflow,incomestatementhave their usual
title as the default; other reports have no title by default. -
The
--subreport-titles=TITLE1|TITLE2|...option customises section
titles for compound reports likebalancesheetand
incomestatement. Titles should be|-separated. Eg:hledger bs --subreport-titles='Aktiva|Passiva'. Or use--subreport-titles=''
to suppress subreport titles. -
The
--period-titles=compact|datesoption customises column
headings for periodic reports.compact(the default) preserves
current behaviour (abbreviated english month/week names within a
single year, explicit dates otherwise).datesalways shows
explicit ISO dates. #2578 -
balance: in single-period reports, account names are now kept
aligned when amounts are wide. (Adam Sardo, #1148) -
balance: single-period HTML/FODS reports now respect the preferred
commodity display style, like other reports.
(Simon Michael, Henning Thielemann, #2584, #2588) -
In
--pivot's value,accountis now accepted as a synonym foracct.
Anddescriptionis documented as a synonym fordesc. -
roiuses a new more robust TWR algorithm.
The previous TWR implementation assumed that an exact valuation of
the investment could be obtained immediately before or after any
cashflow, which is not generally possible.roinow uses the BAI
(Bank Administration Institute) Linked IRR method, valuing the
investment only on dates where an exact valuation is available.
This greatly improves the robustness of TWR computations.
(Dmitry Astapov, #2420)
Timedot
-
A line whose first word looks like a date (three groups of digits
separated by-,/, or.) is now always parsed as a timedot
date line, and shows a clear error if the date is invalid (instead
of silently treating it as a comment or a zero-amount entry). -
Lines with an org heading prefix (eg
**) are parsed more consistently.
Other
-
hledger now requires version 2.3+ of the aeson library, avoiding a
bug where certain data could trigger memory/CPU exhaustion.
(https://haskell.github.io/security-advisories/advisory/HSEC-2026-0007.html) -
setupno longer shows a'"ver' is not recognized ...error message on Windows. -
setupnow shows "all account types exist" as neutral, not a warning. -
setupnow warns if the filesystem does not support appending. #2577 -
statsnow shows a base currency inferred for the journal. -
allow megaparsec >9.8
Docs
- add: balance assignments #2603
- balance:
--gainshows total (realised + unrealised) gain #2049 - check basis: explain strict-comparison rationale, list escape hatches #2636
- Cost basis methods: edits
- Cost basis vs transacted cost: edits
- csv: CSV vs hledger fields, if, if table: rewrites [#2647]
- Directives, Directive effects: consolidate, cleanup, update
- get: document new command
- Lot reporting: many updates
- print layout: rename, expand
- Recording disposals: new section walking through each disposal style
- Report titles: new section
- roi: describe new TWR algorithm (Dmitry Astapov)
- Tags: clarify/restore some missing tags info #1640, #1950
- timedot: rewrite
Examples
examples/csv/github-sponsorships.rulesexamples/lots/lot-entries.journal: fix gain posting
Scripts/addons
- Add sample
getdataandgetpricesscripts for thegetcommand. - Drop the old
hledger-pricehistscript.
API
Hledger.Write.Ods: enable digit grouping in output ifAmountStyledeclares any digit groups. (Henning Thielemann) #2584Hledger.Data.Amount: consolidateshowPriceDirectivehere.
hledger-ui 1.99.3
Breaking changes
Fixes
- On Windows, the
Akey (runninghledger-iadd), theEkey
(running$EDITOR), and the running of info/man/tldr/pager helpers
now correctly quote file paths containing spaces. #2646
Features
cur:COMMqueries now match COMM or any of its commodity aliases (see hledger changelog).
To match only a specific symbol without considering aliases, usesym:SYM.
Aliases are recomputed on each journal reload, so a mid-session
change to alias definitions takes effect next time.
Improvements
- The
Ekey now jumps to the error position with the Zed and Sublime Text editors, also.
(Rostislav Raykov) - allow megaparsec >9.8
- allow vty 6.6+
Docs
API
hledger-web 1.99.3
Breaking changes
- When listening on a non-local-only IP address, hledger-web now
allows only read-only access by default. So if you set a public
address with --host, you may also need --allow=add.
Fixes
- hledger-web now uses aeson 2.3, avoiding a denial-of-service bug in
that library. Previous hledger-web versions, with /add enabled, are
vulnerable to HTTP requests that could trigger memory/CPU exhaustion.
Now it uses a fixed version of aeson (and disables /add by default,
except on local-only addresses).
(https://haskell.github.io/security-advisories/advisory/HSEC-2026-0007.html)
Features
cur:COMMqueries now match COMM or any of its commodity aliases (see hledger changelog).
To match only a specific symbol without considering aliases, usesym:SYM.
Aliases are recomputed on each journal reload, so a mid-session
change to alias definitions takes effect next time.
Improvements
- allow megaparsec >9.8
- allow yesod-core 1.7*
Docs
API
project changes 1.99.3
Docs
- Install: add Building outside the source tree
- AI policy/FAQ/journal: updates, links; start journalling estimated project AI usage
- SPEC-lots: align with the user manual's restructure of Lot reporting
- PLAN-commodity-preferred-symbols, PLAN-ugain
- ACHIEVEMENTS: better star-history link, top 10 apps
- PULLREQUESTS: cross-link to COMMITS and AI
- REGRESSIONS: #2642
- RELEASING, ANNOUNCE, relnotes updates
Tools/infrastructure
- tools/relnotes: demote any markdown headings in changelogs, to fit in release notes
- tools: Shake changelogs: parse inter-release headings more carefully
- cabal.project: consolidate workarounds (haskeline Windows bug #2410, yesod-static bounds bug); general cleanup
- stack: bump dev/release builds and scripts to nightly-2026-06-01 #2410
- stack: ignore all bounds, for now, to allow building with aeson 2.3
- ci: fix old references to
masterin workflows - ci: linux binaries: more workarounds for dependency/bounds bugs
- pr template: update, mention the AI policy
just ai*usage report scripts: vertical output by default; easy conversion from opus tokens to t/kt/Mttools/regen-shelltests.pyscript,regen-shelltestsclaude code skilljust devver: fix- gitignore updates
AI usage
hledger 2.x releases use AI-assisted development;
our AI policy is at https://hledger.org/AI.html.
All changes were steered, reviewed, and tested by Simon or a contributor.
For 1.99.3, mostly opus 4.7 was used. My reporting methods have
changed since last time; here's my current rough estimate of tokens
used per month:
output tokens release
Jan || 771 ||
Feb || 53,229 ||
Mar || 112,112 || 1.99.1
Apr || 686,507 || 1.99.2
May || 2,230,792 ||
Jun || 1,080,010 || 1.99.3
Total || 4,163,421 ||
Average || 693,904 ||
credits 1.99.3
Simon Michael,
Dmitry Astapov,
Henning Thielemann,
Adam Sardo,
Rostislav Raykov.
Install
For all install options, see hledger.org: Install.
You can install hledger from most package managers (but check for a green badge indicating it's up to date).
Or you can build it from source yourself.
Or you can install up to date binaries from this page, by following the appropriate instructions below.
If you find problems with these instructions, please let us know.
All platforms
If you have eget, that's a convenient way to download the right binaries for your machine:
eget simonmichael/hledger --all --pre-release
Otherwise:
GNU/Linux, 64-bit Intel
At the command line:
curl -fLOC- https://github.com/simonmichael/hledger/releases/download/1.99.3/hledger-linux-x64.tar.gz | tar -xzv -f- -C/usr/local/bin hledger hledger-ui hledger-web
hledger --version; hledger-ui --version; hledger-web --version # should show 1.99.3
Mac, 64-bit ARM or Intel
In a terminal window (don't download the binaries with your web browser, they won't get authorised):
On ARM macs:
curl -fLOC- https://github.com/simonmichael/hledger/releases/download/1.99.3/hledger-mac-arm64.tar.gz | tar -xzv -f- -C/usr/local/bin hledger hledger-ui hledger-web
hledger --version; hledger-ui --version; hledger-web --version # should show 1.99.3
On Intel macs:
curl -fLOC- https://github.com/simonmichael/hledger/releases/download/1.99.3/hledger-mac-x64.tar.gz | tar -xzv -f- -C/usr/local/bin hledger hledger-ui hledger-web
hledger --version; hledger-ui --version; hledger-web --version # should show 1.99.3
Windows, 64-bit ARM or Intel
In a powershell window (press WINDOWS-R, powershell, ENTER):
cd ~
curl https://github.com/simonmichael/hledger/releases/download/2.0pre1/hledger-windows-x64.zip -OutFile hledger-windows-x64.zip
Expand-Archive hledger-windows-x64.zip -Force -DestinationPath AppData\Roaming\local\bin
hledger --version; hledger-ui --version; hledger-web --version # should show 1.99.3
Windows 7, 64-bit Intel
These instructions have not been tested recently, please let us know if they work for you:
- click hledger-windows-x64.zip below
- choose Open with Windows Explorer, OK
- click Extract all files
- choose a destination folder - ideally one that appears in
echo %PATH%, likeC:\Windows(though that one will require administrator permission); otherwise, your home directory (C:\Users\YOURNAME) - check "Show extracted files when complete"
- click Extract, wait for the destination folder to open
- find the hledger, hledger-web icons (if you extracted to
\Windows, you'll need to scroll down) - for each icon: double-click, uncheck "Always ask before opening this file", click Run
- close those Explorer windows
- open a command window (press Windows-r, type CMD, press enter)
hledger --version; hledger-ui --version; hledger-web --versionshould show 1.99.3echo # >> .hledger.journalto ensure a default journal file exists. (Important: the doubled >> is needed to avoid overwriting existing data.)
Problems:
- Starting hledger by double-clicking its icon won't work because it needs arguments; run it from the command window instead.
- Starting hledger-web by double-clicking its icon may fail eg because Explorer's command window is too small;
configure that to be larger, or run hledger-web from a command window instead. - hledger or hledger-web may fail to run if there is not enough memory available.
Next steps
Once installed, run hledger, and perhaps read hledger.org: Quick start.