Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rules for detecting special postings

Here's an overview of ways hledger infers things based on certain configurations of postings.

1. journalClassifyLotPostings (Lots.hs)

Detects lot-related postings and tags them with _ptype. Classifications:

PatternTagConditions
acquire_ptype:acquirePositive amount + cost basis {}; OR positive lotful amount (commodity/account has lots: tag)
dispose_ptype:disposeNegative amount + cost basis; OR negative lotful amount, with no opposite-sign counterpart in a different account
transfer-from_ptype:transfer-fromNegative amount + cost basis (or lotful), AND a matching positive counterpart in a different account with same commodity and exact quantity; OR negative lotful amount with no transacted price and an equity counterpart posting (equity transfer)
transfer-to_ptype:transfer-toPositive amount + cost basis with matching negative counterpart; OR positive lotful amount with no @ cost and a matching transfer-from counterpart; OR bare positive asset posting with a matching transfer-from counterpart; OR positive lot posting with an equity counterpart (equity transfer, e.g. opening balances from close --clopen --lots)
gain_ptype:gainPosting to a Gain-type account
short position_ptype:acquire / dispose / transfer-from / transfer-toIn a Liability-typed account the signs above are reversed: a negative lotful or cost-basis amount is an acquire (a short sale), a positive one a dispose (a cover); with no price and an equity counterpart, transfer-from (positive) or transfer-to (negative). Liability postings are never transfer counterparts (see SPEC-lots "Short positions")
same-account transfer_ptype:transfer-from / transfer-toWithin the same account, positive and negative postings with the same commodity and exact absolute quantity are paired as transfers

Detection is per-transaction. Transfer detection uses precomputed maps keyed by (commodity, |quantity|) for O(n) matching.


2. journalTagCostsAndEquityAndMaybeInferCosts (Journal.hs → Transaction.hs)

Detects equity conversion postings and corresponding costful postings. Called twice in the pipeline (before balancing with addcosts=False to detect, after with addcosts=True to add costs when --infer-costs).

PatternTagConditions
conversion posting pair_conversion-postingTwo adjacent postings to accounts declared as Conversion type (type V), each with a single-commodity amount and no cost
costful posting matching conversions_cost-postingA non-conversion posting whose amount matches -ca1 and whose cost matches ca2 (or vice versa), where ca1/ca2 are the conversion pair amounts
costless posting matching conversions_cost-posting(When no costful match exists) A costless single-commodity posting whose amount matches -ca1 or -ca2; must be unambiguous (no other costless posting with the same amount)

In addcosts=True mode, the matched costless posting also gets a TotalCost added.

The _cost-posting tag causes the balancer to strip costs from that posting (preventing double-counting with conversion postings).


3. journalInferEquityFromCosts (Journal.hs → Transaction.hs → Posting.hs)

Detects costful postings that lack corresponding equity conversion postings:

PatternActionConditions
costful posting without conversionsGenerates a pair of _conversion-posting + _generated-posting tagged postingsPosting has cost amounts (@ or @@) AND is NOT already tagged _cost-posting (i.e., not already matched to existing conversion postings)

For each cost amount, two conversion postings are generated under <equityAcct>:<sorted-commodities>: with amounts that offset the cost. Their amounts are rounded to the entry's precision, except in an entry with an _imbalance-posting (see 4), where they keep the exact cost.


4. transactionMaybeAddImbalancePostings (Balancing.hs), with --infer-imbalance

Run on each transaction when the balancer has balanced it (in either of journalBalanceTransactionsHelper's passes; not in balanceSingleTransaction, so not by add).

PatternActionConditions
balanced entry with a tolerated imbalanceGenerates _imbalance-posting + _generated-posting tagged postings to the imbalance account, one per commodity of the imbalanceThe real postings' (or balanced virtual postings') sum at cost, as calculated by transactionCheckBalanced, would not display as zero (shown in full, or with 8 decimal places if a repeating decimal; see amountSetFullPrecisionUpTo), so leftovers of hledger's own division are ignored

The imbalance account is chosen by journalAccountForType: the first account declared with type M/Imbalance, else the alphabetically first account inferred as M (eg equity:imbalance), else equity:imbalance. The generated postings have amountless originals (like inferred amounts), so they don't influence commodity display precisions. In pass 2 (entries with balance assignments) their amounts are also added to the running balances, so balance assertions see them.


Summary of all hidden tags used

TagSet byMeaning
_ptype:acquirejournalClassifyLotPostingsLot acquisition
_ptype:disposejournalClassifyLotPostingsLot disposal
_ptype:transfer-fromjournalClassifyLotPostingsLot transfer source
_ptype:transfer-tojournalClassifyLotPostingsLot transfer destination
_ptype:gaintransactionTagGainPostings / journalAddOrCheckGainPostingsRealised-gain posting in a disposal (user-written, or generated); set aside by the balancer
_cost-postingjournalTagCostsAndEquityAndMaybeInferCosts / journalInferEquityFromCostsHas (or could have) cost matching conversion postings
_conversion-postingjournalTagCostsAndEquityAndMaybeInferCosts / journalInferEquityFromCostsEquity conversion posting
_imbalance-postingtransactionMaybeAddImbalancePostings (--infer-imbalance)Posting to the imbalance account showing a transaction's tolerated imbalance
_generated-postingjournalInferEquityFromCosts / journalAddOrCheckGainPostings / transactionAutoSplitFeeOutflows / preserveParentAssertion / transactionMaybeAddImbalancePostingsMachine-generated posting
_feesplit-postingtransactionAutoSplitFeeOutflowsFee fragment split off a lot transfer's sending posting; hidden by print unless --lots or priced
_lotsplit-postingprocessDisposePosting / processTransferGroupExtra fragment of a posting split across several lots; merged back unless --lots
_lot-parent-assertionpreserveParentAssertionZero-amount posting on the parent account, carrying a balance assertion moved from a posting split into lot subaccounts