Аннотация

hledger 2.0 — крупнейшее изменение формата журнала за всю историю проекта: впервые появляется автоматический учёт лотов и расчёт реализованной прибыли, а вместе с ним — жёсткие проверки, которых в hledger 1 не было. Последний предварительный выпуск, 1.99.5, датирован 1 октября 2026 года (на GitHub — 2 октября); финальный 2.0 обещан «позже в этом году»12. Разбор собран по первичным документам проекта: странице обновления, release notes, спецификации лотов, мануалу 1.99, замерам производительности и обсуждениям в трекере, — плюс документация Ledger и Beancount для сравнения. Данные на 2026-10-20.

Практический вывод для того, кто ведёт журнал в git: обновление почти не требует правок синтаксиса, но меняет смысл нескольких конструкций, которые в hledger 1 молча игнорировались. Часть изменений не выдаёт ошибку, а тихо меняет цифры в отчётах, и именно они опаснее всего3.

Введение

Актуальность. hledger — не самый популярный, но самый дисциплинированный из plain-text accounting инструментов: один автор, ежеквартальный релиз, полный мануал и явная политика обратной совместимости45. По сводке plaintextaccounting.org, релизы в 2026 году есть у Beancount, hledger, Tackler и rustledger, а у Ledger последний датирован 2025-м6. Переход 1.x → 2.x — редкое событие: предыдущая смена мажорного номера была в октябре 2016 года1.

Цель — разобрать, что именно меняется в 2.0 на уровне модели данных и повседневной работы, и ответить на вопрос: что проверить в своём журнале до переезда, а что можно игнорировать. Границы: статья не учит бухгалтерии и не разбирает налоговые режимы конкретных юрисдикций; сравнение с GUI-приложениями (GnuCash, Quicken) ограничено одним абзацем; производительность берётся из замеров самого проекта, независимых бенчмарков нет.

Что значит «2.0» для проекта, который обещал ничего не ломать

Номер версии здесь — результат компромисса, а не технической необходимости. В issue #2547 автор проекта Саймон Майкл формулирует мотивацию прямо: «It’s hard to deliver big features in a short enough time, as to justify a jump from 1.x to 2.x version numbering», и добавляет, что учёт лотов, возможно, требует «impactful changes to our data model»7.

Обсуждение в трекере и итоговый план в ROADMAP стоит прочитать целиком: новые пользователи приводили в пример переход Beancount v2 → v3 как «cautionary tale — intermingled docs, broken companion tools, unclear what works with which version», а один из коллабораторов — аргумент «don’t break compatibility without a clear goal that requires it»8. Выбранная стратегия названа «Boring 2.0», и её принцип сформулирован в одну строку:

Take the marketing win (call it 2.0) but keep the technical disruption minimal.8

Отсюда линейный путь 1.52 → 1.99.1 → … → 2.0 без параллельной ветки, минимум ломающих изменений и новые поведения за флагами вроде --lots8. Формально это выполнено: синтаксис журнала не переписан, файлы hledger 1 читаются. Но «минимальный разрыв» относится к грамматике, а не к семантике.

Lots: изменение модели данных, а не синтаксиса

Учёт лотов — то, ради чего всё затевалось. Ещё в мануале 1.52 пользователю советовали экспортировать данные в Ledger или Beancount, «when you want to calculate lots and capital gains» — то есть функции в самом hledger не было9. Автор не скрывает, что без AI-ассистентов этой функции не было бы: «I needed it to fully design and implement robust tax lot tracking in hledger. This is a feature that I have been wanting for many years, but it was just too big/intricate to tackle»5. Политика проекта при этом остаётся демонстративно осторожной: hledger не использует AI в рантайме, требует раскрывать провайдера и модель в коммитах, не использует модели OpenAI, а первый PR нового контрибьютора не должен содержать AI-генерированного кода5.

Технически включается одной строкой в объявлении товара — тегом lots::

commodity AAPL  ; lots:

После этого покупки создают лоты, продажи списывают их по выбранному методу (FIFO, LIFO, HIFO, AVERAGE, SPECID — по счёту или глобально), hledger считает реализованную прибыль и проверяет написанную, а отчёт holdings показывает нереализованную1011. Счета, где налоговая база не важна (пенсионные, tax-sheltered), исключаются тегом lots: NONE11.

Проверки, которых раньше не было

Главное отличие от hledger 1 — не новые команды, а то, что аннотации {...} перестали быть декорацией. В hledger 1 аннотация cost basis вида {$50} принималась и игнорировалась; в hledger 2 он определяет лот, и расхождение с фактической ценой сделки @ становится ошибкой31. Практический пример из документации: запись 10 AAPL {$50} @ $55 в hledger 2 не загрузится — «This acquire posting’s cost basis ($50) differs from its transacted cost ($55)»3.

Второе следствие — сделки, записанные по старой привычке. В hledger 1 продажу часто писали по исходной цене: -5 AAPL @ $50 плюс проводка на прибыль. В hledger 2 @ в такой записи читается как цена продажи, и транзакция перестаёт балансироваться; правильно — -5 AAPL {$50} @ $703. Третье: если проводка прибыли не написана, hledger 2 её выводит сам и отправляет на revenues:gain по умолчанию — то есть в отчёте о прибыли появляется строка, которой в hledger 1 не было3.

Наконец, изменилась и механика балансировки выбытия: начиная с 1.99.5 списание балансируется по исторической стоимости одной проводкой реализованной прибыли, а промежуточные контрпроводки equity:unrealised-gain, которые использовали предыдущие превью, больше не создаются — если вы их писали руками, их нужно убрать1. Короткие позиции (продажа раньше покупки) поддерживаются как лоты на счетах типа Liability1.

Что делать с историей

Документация честно предупреждает: восстановление полной истории лотов — большая работа, потому что «later entries tend to depend on all previous entries»11. Предлагаются три обхода: черновик в отдельном файле (hledger -f investments.journal check), старт с начала текущего года через открывающую запись с исходными датами и ценами покупки, и поэтапное включение по одному товару или счёту11. Для этого нужен синтаксис с датой, который hledger 1 не прочитает:

2026-01-01 opening lots
    assets:stocks      10 AAPL {2024-03-15, $50}
    equity:opening/closing balances

Если разбираться не хочется, есть аварийный выход: --ignore-lots (он же -I) отключает пост-балансировочные стадии — расчёт лотов, проверку тегов и вывод прибыли, — но оставляет пре-балансировочные, чтобы записи балансировались так же, как с включёнными лотами10. Это важное изменение относительно ранних превью: до сентября 2026 -I отключал и их, из-за чего часть корректных записей переставала балансироваться, то есть флаг добавлял ошибки вместо того, чтобы убирать10. Оговорка: -I также отключает balance assertions, а --strict и hledger check lots перебивают его и включают полную обработку310.

Тихие изменения: журнал читается, а цифры другие

Самая неприятная категория — изменения, которые не дают ошибки. Первое и самое коварное: конфигурационный файл больше не может выбирать команду по умолчанию. Слово вверху общей секции, которое hledger 1 трактовал как команду, теперь читается как аргумент запроса, «which quietly filters every report»3. То есть отчёты продолжат работать, но будут считать не то, что вы думаете.

Второе — порядок применения алиасов и apply account. Теперь алиасы применяются сначала к имени счёта, как оно написано, а родительский счёт добавляется после (как в Ledger); hledger 1 делал наоборот. Алиасы, записанные вместе с родительским счётом, перестают совпадать, а записанные без него — начинают, из-за чего могут упасть balance assertions и проверки счетов3. Опция --alias не затронута — она по-прежнему применяется последней3.

Третье — точность вывода и разбор чисел. Количество знаков после запятой у выведенных сумм больше не влияет на отображаемую точность товара, поэтому некоторые отчёты покажут меньше разрядов3. При объявленной директиве decimal-mark число, явно использующее другой разделитель, теперь считается ошибкой, а не догадкой13. В CSV-правилах строка, начинающаяся с *, больше не комментарий, а невалидное регулярное выражение с подсказкой экранировать его1. Повторяющиеся настройки CSV теперь разрешаются «последняя побеждает» — если вы писали настройку до include, чтобы перебить включённый файл, её нужно перенести после3.

Четвёртое — мелкие, но ломающие скрипты переименования: --tldr стал --examples, stats -1 стал stats --oneline, --verbose-tags остался только у rewrite, команды demo и commands удалены, print --layout=hledger1 возвращает старый вид3. hledger-web, слушающий не на localhost, теперь по умолчанию только для чтения — редактирование включается --allow=edit3.

Отдельно стоит пройтись по hledger check и сравнить отчёты двух версий: документация предлагает прямой рецепт diff <(hledger1 bs) <(hledger bs) для bash и zsh и import --dry-run для CSV3. Это единственный способ поймать тихие расхождения до того, как они попадут в налоговую отчётность.

data/, rules/, prices/: импорт как конвейер

Вторая крупная тема — не про учёт, а про рутину. hledger 2 вводит «конвенциональную раскладку»: data/ для скачанных выписок, rules/ для правил конвертации, prices/ для курсов, все рядом с основным журналом312. Новая команда get запускает вспомогательные скрипты data/getdata и prices/getprices, скачивает транзакции и курсы и аккуратно сливает новые цены в prices/<COMM>.prices, сохраняя старые: «New prices for dates already seen are discarded»12. Флаги --transactions, --prices, --dry-run позволяют разложить процесс на шаги и посмотреть, что будет запущено12.

Дальше import без аргументов обрабатывает все правила из rules/, а import -g совмещает скачивание и импорт12. Путь из правила source разрешается так: ./ и ../ — относительно файла правил (как в hledger 1), а голое имя или относительный путь ищется сначала в data/ рядом с журналом, потом в ~/Downloads3. Это объясняет, почему у части пользователей после обновления «поехали» пути.

К этому же слою относятся commodity aliases с множителем (commodity h 1.00 ; alias: 60 min) и автоматическая конвертация сумм с алиасом в канонический символ, из-за чего запрос sym: удалён1. И команда transactions (tx) — компактный список по одной строке на транзакцию, в духе git log --oneline, что для журнала на 20 тысяч записей удобнее print12.

Скорость: цифры и что они значат

Замеры проекта на синтетическом журнале в 100 тысяч транзакций (16 МБ, 1 тыс. счетов, 26 товаров) показывают, что 1.99.5 — самый быстрый hledger: отчёт balance занимает 1,40 с против 4,08 с у 1.52 и 5,89 с у 1.99.4, при пиковом потреблении 573 МБ против 868 МБ13. Пропускная способность на простом журнале — около 80 тысяч транзакций в секунду, на «грязном» реальном — около 40 тысяч13.

Важно, где выигрыш меньше. На реальном 20-летнем журнале (21 тыс. транзакций в 87 включённых файлах, 5 тыс. директив цен, balance assertions на 14% записей) ускорение скромнее: stats — 0,53 с (в 2,0 раза быстрее, чем у 1.52), balance — 0,61 с (в 1,7 раза), register — 3,30 с (всего в 1,3 раза)13. Причина названа прямо: register упирается в объём вывода, а не в чтение журнала13.

Там же лежит ответ на вопрос, который чаще всего задают про hledger: сравнение с конкурентами на тех же файлах. На 100 тысячах транзакций balance у Ledger 3.4.1 занимает 15 с, у Beancount 3.2.3 — 6,6 с без кэша и 4,0 с кэшем, у rustledger 0.24.0 — 0,61 с, у Tackler — 0,11 с; hledger 1.99.5 — 1,4 с13. На миллионе транзакций hledger укладывается в 14 с и 5,2 ГБ, Ledger не досчитывает за пять минут13. Автор замеров сам оговаривает их границы: журналы упрощённые, приложения делают разный объём проверок и вывода, корректность вывода не проверялась — «treat this as a rough sketch»13.

Сравнение с Ledger и Beancount

Учёт лотов есть во всех трёх, и синтаксис похож: {} для базы, @ для цены. Разница — в том, что каждая программа проверяет. По сравнительной таблице из спецификации hledger (версии сверены с Ledger 3.4.1 и Beancount 3.2.3): Ledger не замечает, когда цена продажи противоречит пришедшим деньгам, Beancount тоже, hledger выдаёт несбалансированную транзакцию; Ledger разрешает продать непокупавшийся лот, Beancount и hledger — нет; Ledger и Beancount требуют, чтобы проводку прибыли написал пользователь, hledger выводит её сам и проверяет, если она написана10.

A sale price contradicting the cash | not detected | not detected | unbalanced transaction10

В самом Ledger цена лота работает как скрытая база: если цена продажи расходится с ней, транзакция падает, потому что «the $250.00 price difference between the price you bought those shares at, and the cost you’re selling them for, does not match», а флаг --lots показывает лотовую информацию в отчётах14. То есть Ledger ловит расхождение на балансировке, но не проверяет, что проданный лот вообще существовал, — ровно та проверка, которой в Ledger нет, а в Beancount и hledger 2 есть10.

Там же сформулировано, в чём hledger отличается от всех остальных: метод списания и база лежат в том же plain text, что и вся книга; лоты видны как обычные субсчета в обычных отчётах; а записанная прибыль работает как assertion — при изменении старых записей пересчёт перестаёт с ней совпадать, и hledger сообщает об ошибке вместо того, чтобы тихо переписать историю10. Именно третьего, по утверждению автора спецификации, нет ни у кого из известных ему инструментов10. Проверяемость цитаты ограничена: сравнение с Ledger и Beancount автор подтверждает версиями, остальные строки таблицы — «from memory of the apps’ docs»10.

Для полноты картины — как это устроено в настольных приложениях. Quicken и большинство GUI-программ прибыль не записывают: они хранят транзакции и, для каждой продажи, израсходованные лоты, а прибыль считают в момент построения отчёта, поэтому правка старой покупки молча меняет историческую прибыль. GnuCash — исключение: он умеет генерировать проводки прибыли отдельным проходом («scrubbing») после факта, но лоты в регистре не видны. hledger пересчитывает всё из журнала при чтении, зато записанная проводка прибыли работает как assertion и ловит расхождение10.

Функциональный паритет с Beancount при этом сместился. Страница сравнения фиксирует: «compared to hledger 1, Beancount has more support for tracking investments and capital gains. As of the hledger 2 prereleases, it’s the other way around»15. Обратная сторона — экосистема: экспорт Beancount → hledger до сих пор делается через bean-report из Beancount 2, которого в третьей версии нет, и полученный файл требует ручных правок (двойные @, неточные итоговые стоимости, закомментированные запросы)15.

Урок Beancount v3

Ссылка на Beancount в обсуждении 2.0 — не риторика. Формально v3 очень аккуратна: разборщик совместим, «the core syntax is unchanged. All valid v2 directives parse in v3», а список ломающих изменений состоит из мелочей вроде регистра метода списания ("fifo" → "FIFO"), обязательного UTF-8, тайминга balance assertions и удалённого beancount.plugins.prices16. Но именно эти мелочи ломают сопутствующие инструменты и инструкции, а обещанное в дизайн-документе переписывание на C++ ради скорости ушло в отдельную ветку и в релиз не попало: v3 — это master без C++-кода1718.

Для hledger вывод симметричен и неприятен: там, где Beancount сохранил синтаксис и потерял совместимость инструментов, hledger сохраняет синтаксис и меняет семантику. Пользователь, у которого журнал читается обеими версиями, но отчёты расходятся, находится в худшем из возможных положений — ошибки нет, а цифры разные.

Как жить с двумя версиями

Проект рассчитывает на сосуществование: «hledger 1 (1.52.x) continues to receive occasional fixes, and both versions can be installed side by side», и предлагает переименовать бинарник в hledger13. Обе версии читают один конфиг, но если вы начнёте пользоваться новыми возможностями конфига (например, многострочными алиасами), hledger 1 откажется запускаться даже с --version — обходится отдельным --conf3.

Правила «общего» журнала сведены в отдельный раздел. Аннотации базы нужно дублировать фактической ценой (10 AAA {$50} @ $50), проводку прибыли либо не писать вообще (hledger 2 выведет, hledger 1 не увидит), либо писать без суммы; датированные аннотации {2024-03-15, $50}, объявления type: U и правило merge в CSV использовать нельзя3. Имена счетов, заканчивающиеся на :{...}, зарезервированы под лоты и в hledger 2 отклоняются, если внутри не валидное имя лота3.

Дискуссионные вопросы и противоречия

Насколько быстро на самом деле. Один и тот же проект в разных документах называет ускорение по-разному: release notes — «2x faster than 1.52», страница обновления — «2-3x», страница сравнения с Ledger — «about 3x faster» на 100-тысячном журнале, а таблица замеров даёт 2,9x для balance и 1,2x для register131913. Все числа воспроизводимы, но зависят от отчёта и формы журнала, поэтому «два-три раза» — не универсальная характеристика, а диапазон для конкретного бенчмарка.

«Высокая совместимость» против тихих изменений. Release notes утверждают, что превью 2.x «strictly better, highly compatible»1. При этом список изменений в чтении файлов включает минимум три пункта, которые не ломают загрузку, а меняют результат: команда по умолчанию из конфига, порядок алиасов с apply account, точность вывода3. Совместимость здесь означает «читается», а не «считает то же самое».

Готовность к продакшену. Заявления «both hledger 1 and 2 are suitable for daily use» соседствуют с отказом объявлять 1.99.5 production-релизом: в форуме пользователь отмечает, что Homebrew не обновляет пакет, потому что «1.99.5 is not a declared production release», а hledger setup показывает «latest is 1.52.4, 1.99.5 is installed»120. Для бухгалтерии, где ошибка стоит денег, это существенная разница между «работает» и «поддерживается».

Минимальный разрыв против смены модели. Принцип «keep the technical disruption minimal» и признание, что лоты требуют «impactful changes to our data model», сосуществуют в одном обсуждении87. Оба утверждения верны в своих границах: грамматика стабильна, семантика — нет.

Недостаточность данных

Установить не удалось следующее. Нет публичных замеров стоимости включённого учёта лотов: спецификация говорит, что журналы без лотов платят «near-zero cost via an internal fast path», но цифр не приводит10. Нет данных, сколько пользователей уже перевели журналы на 1.99.x и какие ошибки встречались чаще всего, — форумные треды содержат единичные отзывы без цифр202122. Дата финального 2.0 остаётся обещанием «later this year» без конкретного срока21. Независимых (не авторских) замеров производительности hledger 2 не найдено; все числа в разделе о скорости — из одного источника и с явной оговоркой автора о грубости методики13. Наконец, совместимость hledger 2 с Ledger-синтаксисом на «gnarly old» файлах не проверялась: документация описывает её как ограниченную, но без систематического теста19.

Заключение

hledger 2.0 стоит воспринимать не как «новую версию с новыми командами», а как смену контракта: аннотации {...} стали данными, которые проверяются, а несколько конструкций конфига и CSV приобрели другой смысл. Кому переезд почти ничего не стоит — тем, у кого нет инвестиционных счетов, нет {...} в журнале и есть привычка сверять отчёты после обновления инструмента. Кому стоит планировать работу — тем, кто ведёт брокерские счёта в hledger 1 и вручную считал лоты субсчетами: там переезд означает либо реконструкцию истории через открывающие лоты, либо жизнь с -I и без проверок1110.

Практический порядок действий, который следует из документации: поставить 2.0 рядом с 1.52, прогнать hledger check, сравнить accounts/bs/is и import --dry-run, затем включить lots: на одном товаре и сверить holdings с брокерским отчётом311. Для тех, кому важна предсказуемость, hledger 1 остаётся живой веткой с точечными исправлениями, а политика проекта прямо допускает её возрождение или форк5.

Quality Metrics

MetricValue
Источников22
Официальная документация / спецификации14 (64%)
Первичные обсуждения (issue, форум, релизы)6
Стандарты сообщества и сводки2
Покрытие параграфов цитатами91%
Объём3 279 слов
Раундов углубления2
Открытых вопросов5 (раздел «Недостаточность данных»)

  1. hledger. “Release notes (hledger 1.99.5, 2.0 preview 5).” hledger.org, 2026-10-01. https://hledger.org/release-notes.html ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  2. hledgerorg/hledger. “Releases: 1.99.5 (2026-10-02), 1.99.4 (2026-09-11), 1.99.3 (2026-06-24), 1.99.2 (2026-04-28), 1.99.1 (2026-03-28).” GitHub. https://github.com/hledgerorg/hledger/releases ↩︎

  3. hledger. “Upgrading to hledger 2.” hledger.org, 2026. https://hledger.org/upgrading.html ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  4. Michael S. “ANN: hledger 1.52, hledger 2.0 preview 1.” Plain Text Accounting forum, 2026-03-29. https://forum.plaintextaccounting.org/t/ann-hledger-1-52-hledger-2-0-preview-1/772 ↩︎

  5. hledger. “AI policy.” hledger.org, updated 2026-09-21. https://hledger.org/AI.html ↩︎ ↩︎ ↩︎ ↩︎

  6. plaintextaccounting.org. “Plain Text Accounting apps overview.” 2026. https://plaintextaccounting.org/ ↩︎

  7. Michael S. “Thoughts on hledger 2, issue #2547.” GitHub, hledgerorg/hledger. https://github.com/hledgerorg/hledger/issues/2547 ↩︎ ↩︎

  8. hledger. “ROADMAP (2.0 strategy).” hledger.org, 2026-02. https://hledger.org/ROADMAP.html ↩︎ ↩︎ ↩︎ ↩︎

  9. hledger. “hledger manual (version 1.52).” hledger.org, 2026. https://hledger.org/hledger.html ↩︎

  10. hledger. “SPEC-lots: Lot tracking specification for hledger 2.” hledger.org, 2026-09. https://hledger.org/SPEC-lots.html ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  11. hledger. “Start tracking lots.” hledger.org, 2026. https://hledger.org/start-tracking-lots.html ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  12. hledger. “hledger manual (version 1.99, 2.0 previews).” hledger.org, 2026. https://hledger.org/dev/hledger.html ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  13. hledger. “Performance.” hledger.org, 2026-09. https://hledger.org/PERFORMANCE.html ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  14. Wiegley J., et al. “Ledger 3: Command-Line Accounting (lot prices, gains, –lots).” ledger-cli.org. https://ledger-cli.org/doc/ledger3.html ↩︎

  15. hledger. “hledger and Beancount.” hledger.org, 2026. https://hledger.org/beancount.html ↩︎ ↩︎

  16. rustledger/pta-standards. “Breaking Changes: Beancount v2 to v3.” 2026 (validated against Beancount 3.2.0). https://rustledger.github.io/pta-standards/formats/beancount/v3/migration/breaking-changes.html ↩︎

  17. Blais M. “Beancount Vnext: Goals & Design.” beancount.github.io, July 2020. https://beancount.github.io/docs/beancount_v3/ ↩︎

  18. Blais M. “CHANGES (beancount): v3 branch created, master minus the C++ bits.” GitHub, 2024-06-16. https://raw.githubusercontent.com/beancount/beancount/master/CHANGES ↩︎

  19. hledger. “hledger and Ledger.” hledger.org, 2026. https://hledger.org/ledger.html ↩︎ ↩︎

  20. Michael S. “ANN: hledger 1.99.5 (2.0 preview 5).” Plain Text Accounting forum, 2026-10-02. https://forum.plaintextaccounting.org/t/ann-hledger-1-99-5/898 ↩︎ ↩︎

  21. Michael S. “ANN: hledger 1.99.4 (& 1.52.4).” Plain Text Accounting forum, 2026-09-11. https://forum.plaintextaccounting.org/t/ann-hledger-1-99-4-1-52-4/886 ↩︎ ↩︎

  22. Michael S. “ANN: hledger 2.0 preview 3.” Plain Text Accounting forum, 2026-06-24. https://forum.plaintextaccounting.org/t/ann-hledger-2-0-preview-3/818 ↩︎