Back to catalog

hledger

Import bank CSVs and query the double-entry ledger.

Category 📒 Hledger
Version v1.2.0
hledgerfinanceaccountingreconciliationinvestments

hledger Skill

Manage Joseph's double-entry bookkeeping via hledger. Located at wiki/wealth/hledger/. hledger 1.52.4 is installed at ~/bin/hledger.

When to Use

  • Joseph drops a new CSV in source-files/ and wants it imported
  • Joseph asks about spending, balances, or category breakdowns
  • Import rules need updating for new payees
  • Reconciliation or balance check fails
  • Importing stock/MF transactions from broker exports (ICICI Direct, Zerodha)
  • Syncing holdings.csv from hledger
Don't use for: LLM-powered interpretation of financial patterns

Prerequisites

  • hledger binary at ~/bin/hledger (v1.52.4)
  • CSV files in wiki/wealth/hledger/source-files/
  • Import rules in wiki/wealth/hledger/import-rules/

Directory Layout

wiki/wealth/hledger/
├── main.journal          ← entry point
├── accounts.journal      ← chart of accounts
├── balances.journal      ← opening/reconciled balances
├── journals/
│   ├── 2025/             ← per-account journals
│   └── 2026/             ← per-account journals
├── import-rules/         ← hledger CSV import rules
├── scripts/              ← preprocessing + import scripts
└── source-files/         ← CSV/XLS from bank portals
    └── invest/           ← investment CSVs (ICICI, Zerodha)
        ├── done/         ← imported files
        ├── fund-codes.csv ← Zerodha MF short code mapping
        ├── convert-zerodha-stocks.py
        ├── convert-zerodha-mf.py
        ├── apply-zerodha-mf.py
        └── sync-holdings.py

How to Run

Import a new CSV

cd wiki/wealth/hledger
~/bin/hledger -f journals/2026/<account>.journal \
    import source-files/<file>.csv \
    --rules-file import-rules/<account>.rules

Check balance assertions

~/bin/hledger -f journals/main.journal check

Query balances

~/bin/hledger -f journals/main.journal bal --depth 2
~/bin/hledger -f journals/main.journal bal Expenses:Travel --monthly
~/bin/hledger -f journals/main.journal print desc:"Singapore"

Import investment transactions

See references/investment-import.md for the full workflow.

Sync holdings.csv from hledger

python3 wiki/wealth/hledger/source-files/invest/sync-holdings.py

Or dry-run:

python3 wiki/wealth/hledger/source-files/invest/sync-holdings.py --dry-run

Procedure: Import New CSV

1. Place CSV in source-files/

2. Detect format — read first line:

  • S No.,Value Date,... → ICICI 8-col → run scripts/preprocess-icici-8col.py
  • S.N.,Tran. Id,... → ICICI Current → run scripts/strip-commas.py
  • Tran Date,CHQNO,... → Axis → use as-is
  • Other → use as-is

3. Preprocess if needed (step 2)

4. Import with hledger import command (above)

5. Check with hledger check

6. If check fails: report error to user, do NOT auto-fix

Completion criteria: hledger check exits 0 with no output.

Zerodha MF Import Workflow

Zerodha MF CSVs use commodity tracking (quantity + cost basis), not flat amounts.

Fund codes (fund-codes.csv)

Location: source-files/invest/fund-codes.csv

Mapping: short_code → full_name → isin. When importing a new fund:

1. Add row to fund-codes.csv

2. The converter script uses this for commodity naming

Current codes:

  • PPFCF-DP — Parag Parikh Flexi Cap Fund - Direct Plan
  • PPFCF-DPG — Parag Parikh Flexi Cap Fund - Direct Plan Growth (same ISIN as above)
  • PP-LCF — Parag Parikh Large Cap Fund
  • MOMF-DG — Motilal Oswal Midcap Fund - Direct Growth
  • AX-LF-DG — Axis Liquid Fund - Direct Growth
  • IISCF-DG — Invesco India Small Cap Fund - Direct Growth
  • PPAF-DPG — Parag Parikh Arbitrage Fund - Direct Plan Growth

Import steps

cd wiki/wealth/hledger

1. Convert CSVs to staging (dry-run)

python3 source-files/invest/convert-zerodha-mf.py

2. Review staging files in source-files/invest/to-import/zerodha/staging/

3. Apply to journals

python3 source-files/invest/apply-zerodha-mf.py

4. Verify

~/bin/hledger -f journals/main.journal check

Account structure

Per-fund sub-accounts: Assets:Invest:MFs:Zerodha:{Person}:{FundCode}

Persons: Jerry, Josh, JJU, Rani

Bank accounts: Assets:Bank:ICICI:{Person} (all ICICI)

CRITICAL: Anti-Loop Rules

The #1 problem: agent loops burn tokens on import failures. Follow these rules strictly:

1. Max 3 import attempts per session. If the 3rd attempt fails, STOP and give a diagnostic report.

2. Never auto-fix import rules in a loop. If import fails:

  • Read the error
  • Tell Joseph what the fix is
  • Wait for Joseph to confirm before changing anything

3. Use the script (scripts/hledger-import.sh) for automated imports — it has built-in max-3 guard.

4. Separate deterministic from interpretive:

  • Import/check/query → shell commands (zero tokens)
  • "Why did X happen?" → LLM interpretation (minimal tokens)

5. One change per round-trip. Make one rule fix, Joseph runs the script, we see the result. Never batch multiple rule changes.

Privacy Rules

  • Don't post specific amounts in Slack threads — use category names and percentages
  • Structural work is safe — rule audits, duplicate detection, category analysis
  • Amounts stay in journal files on the server, not in conversation
  • For sensitive queries, Joseph should run hledger on his Mac directly

Import Rules

Rules follow hledger v1.52 last-match-wins convention:

  • Catch-alls go FIRST (e.g., account2 expenses:unknown)
  • Specific overrides go LAST (e.g., if ZERODHA BROKING → Income:Invest:Gains)
  • common.rules is included by each bank-specific rules file
When adding a new rule: append at the END of the specific rules file, after include common.rules.

Investment Accounts

ICICI Direct (Stocks)

  • Account: Assets:Invest:Stocks:ICICIDirect:TICKER
  • 62 stock accounts (per-stock commodity tracking)
  • Transactions: journals/YYYY/icici-stocks.journal
  • Conversion script: source-files/invest/convert-icici-stocks.py

Zerodha (Stocks)

  • Account: Assets:Invest:Stocks:Zerodha:PERSON:TICKER
  • 4 sub-accounts: JJU, Rani, Jerry, Josh
  • 15 stock accounts (per-stock commodity tracking)
  • Transactions: journals/2026/zerodha-stocks.journal
  • Conversion script: source-files/invest/convert-zerodha-stocks.py

Mutual Funds (Zerodha)

  • Account: Assets:Invest:MFs:Zerodha:PERSON:FUNDCODE
  • Per-fund sub-accounts with commodity tracking (quantity + cost basis)
  • Transactions: journals/YYYY/investments.journal
  • Conversion script: source-files/invest/convert-zerodha-mf.py
  • Fund codes: source-files/invest/fund-codes.csv

Mutual Funds (ICICI Direct)

  • Account: Assets:Invest:MFs:ICICIDirect:JJU
  • Flat accounts (no commodity tracking)
  • Transactions: journals/YYYY/investments.journal

Pitfalls

  • Last-match-wins: hledger v1.52 reverses normal first-match order. Put broad rules first, specific rules last.
  • Balance assertions: if hledger check fails, the journal has a balance mismatch. Don't ignore it.
  • XLS files: not supported by hledger. Convert to CSV first.
  • Duplicate entries: check for same-date-same-amount entries before importing.
  • Multi-commodity accounts: balance assertions don't work well with multi-commodity accounts. Keep accounts single-commodity where possible.
  • Quoted commodities: tickers with digits (e.g., MON100) need quoting in hledger: "MON100"
  • Don't double-apply: the apply script creates backups. If re-running, restore from the ORIGINAL backup first.
  • Never add include lines for files that don't exist yet. A previous session added ICICI Direct MF includes to 2026.journal pointing to source-files/invest/to-import/icici-direct/ — the directory was never created, blocking hledger-web. Only add includes after the target file is in place.

Verification

After any import:

1. hledger check passes (exit 0, no output)

2. hledger bal --depth 2 shows expected totals

3. No new expenses:unknown entries (grep journals for it)

After investment imports:

1. Spot-check 3 stocks: hledger bal Assets:Invest:Stocks:ICICIDirect:TICKER

2. Compare with holdings.csv: python3 source-files/invest/sync-holdings.py --dry-run

3. Verify no duplicates across brokers