Skip to content

Documentation

Tagging

Hforecast uses hledger tags on journal postings to classify income and deductions for tax calculations.

How tags work

Tags serve a dual role:

  1. Output: When the simulation creates transactions (paychecks, contributions, withdrawals), it tags each posting with the appropriate classification.
  2. Input: The tax engine reads year-to-date totals by querying journal tags (e.g., tag:earnings=income to compute gross income).

In annual mode, this is a closed loop — the simulation creates all transactions, so tags are always present. In daily mode, the simulation starts from an existing journal with real transactions. If those transactions lack the expected tags, tax calculations will be wrong.

Default tags

TagMap field Default value Used for
earnings earnings:income Gross income (salary, wages)
preTaxDeductions deductions:pre-tax 401k contributions, HSA contributions
postTaxDeductions deductions:post-tax Post-tax payroll deductions
medicareExemptDeductions medicare-exempt:yes Deductions exempt from Medicare tax
preTax401kContributions 401k:contribution 401k-specific contributions
retirementWithdrawal withdrawal:retirement Withdrawals from retirement accounts
opening oc:open Opening balance transactions
closing oc:close Closing balance transactions

Tagging existing transactions

If you use daily mode with an existing journal, tag your income and deduction postings. For example, a paycheck entry:

2025-01-15 Employer Paycheck
                           $-5000  ; earnings:income
    401k                $500  ; deductions:pre-tax, 401k:contribution
                                $200  ; deductions:pre-tax
                   $1000
                      $400
            $310
                    $72
                     $2518

The tags after the semicolons (;) are what the tax engine reads.

Tagging a loan account

Loans (mortgages, auto loans, student loans) are declared by tagging their account directive, not individual postings. The simulation reads these tags to build an amortization schedule: a level monthly payment that draws the balance down over the remaining term, splitting each payment into interest and principal.

Tag the liability’s account directive with hforecast-account-type:loan plus the loan’s terms:

account liabilities:mortgage:home
    ; hforecast-account-type:loan
    ; hforecast-loan-rate:6.5
    ; hforecast-loan-term:30
    ; hforecast-loan-start:2020-01-01

Or on one line, comma-separated (hledger reads a tag value up to the next comma):

account liabilities:mortgage:home  ; hforecast-account-type:loan, hforecast-loan-rate:6.5, hforecast-loan-term:30, hforecast-loan-start:2020-01-01

The loan tags are:

Tag Value Meaning
hforecast-account-type: loan Marks the account as an amortizing liability
hforecast-loan-rate: a percentage, e.g. 6.5 for 6.5% The annual interest rate
hforecast-loan-term: years, e.g. 30 (fractional allowed) The loan’s original term
hforecast-loan-start: a date, YYYY-MM-DD When the loan was originated
hforecast-property: a property account name On a loan account, links it to the property it finances (paid off on that property’s sale)

Two conventions are worth calling out:

  • The rate is a percentage, not a fraction. Write hforecast-loan-rate:6.5 for 6.5%, not 0.065. (This differs from the YAML interestRate: field, which is a fraction.)
  • The term is the original term; the start date shortens it. A 30-year mortgage opened in 2020 does not have 30 years left. Give hforecast-loan-start: and the tool subtracts the time already elapsed, so a plan run in mid-2026 sees ~23.5 years remaining and amortizes the current balance over that. Omit hforecast-loan-start: and hforecast-loan-term: is taken as the years still remaining — you then have to shorten it by hand each year.

The balance itself is not a tag: seed it either from the journal (balance: infer-from-journal in YAML, which reads the account’s current balance) or as a literal opening amount.

Linking a mortgage to its property

So a property sale can pay off the right loan, tag the loan account with hforecast-property: pointing at the property it finances (the hub-and-spoke direction: the loan points at the property, and a property can carry more than one linked lien):

account assets:real-estate:home    ; hforecast-account-type:property
account liabilities:mortgage:home  ; hforecast-account-type:loan, hforecast-property:assets:real-estate:home, hforecast-loan-rate:6.5, hforecast-loan-term:30, hforecast-loan-start:2020-01-01

All of these terms can also be given in YAML instead of tags (interestRate:, term:, propertyAccount:); the tag form is convenient when you keep the facts next to the account in the journal itself. Tags and YAML compose — YAML wins where both are present.

Customizing tags

Override the defaults in the tags section of your hforecast.yaml:

tags:
  earnings: type:salary
  preTaxDeductions: category:pretax

Automatic tag detection

When inferFromJournal is enabled (the default), the tool scans your journal for tag conventions on income and retirement account postings. If it finds consistent tags, it maps them to the TagMap automatically. If your income postings have no tags at all, a warning is printed to stderr.