zfin/docs/guides/handle-a-stock-split.md
Emil Lerch f1cabdcf9a
All checks were successful
Generic zig build / build (push) Successful in 4m54s
Generic zig build / deploy (push) Successful in 19s
Generic zig build / publish-macos (push) Successful in 32s
add stock split guide with full rationale
2026-07-06 14:25:56 -07:00

5.2 KiB

Handle a stock split

Goal: keep your holdings correct through a stock split without breaking the history that zfin's other commands rely on.

The one rule: when a stock you hold splits, do not edit the share count on your existing lot. Leave the lot exactly as you transacted it, and tell zfin about the split with one line in metadata.srf. zfin does the rest on read.

Why not just change the share count?

It seems natural: NVDA does a 10:1 split, your 100 shares become 1,000, so you open portfolio.srf and change shares:num:100 to shares:num:1000. Don't -- here is what that quietly breaks.

Your portfolio file is history, and zfin reads the history, not just the latest version. If you are utilizing all zfin features, portfolio.srf lives in git, and several commands diff past commits of it:

  • contributions attributes what changed between two commits to new money vs. market movement. It works on the raw share/lot changes in the diff.
  • compare and audit read git history the same way.

A split changes your share count but not your money: 100 shares at $400 and 1,000 shares at $40 are the same $40,000. But if you edit the lot from 100 to 1,000 and commit, the git diff shows +900 shares. contributions cannot tell that apart from you buying 900 shares, so it reports a large phantom contribution that never happened -- and it is now baked into your commit history permanently.

Snapshots make it worse. zfin snapshot writes immutable, point-in-time records of your portfolio under history/. A snapshot taken before your edit recorded 100 shares; after the edit, your live file says 1,000. The two no longer agree, and you cannot fix the old snapshot -- it is a frozen record of a past day, and compare / history read it as-is.

Both git history and snapshots are append-only truth: undoing the damage means rewriting git history (dangerous) or hand-editing frozen snapshot files (error-prone). So the safe design is simple -- a split must never touch your recorded shares. It is a derivation, not an edit.

What to do instead

Leave the lot alone. Add a per-symbol splits_current_through date to that symbol's row in metadata.srf:

#!srfv1
symbol::NVDA,sector::Technology,geo::US,asset_class::US Large Cap,splits_current_through::2020-01-01

Set the date to when that symbol's recorded shares were last accurate -- for a lot you entered as transacted, that is on or around the purchase. zfin then applies every split after that date to compute the effective (split-adjusted) share count on the fly. Your portfolio.srf, git history, and snapshots stay exactly as transacted; only the displayed and valued shares reflect the split.

You can see the split zfin will apply with zfin splits:

zfin splits NVDA

Copying post-split numbers from your brokerage is fine

Brokerages do restate: after a split, Fidelity or Schwab show you the post-split share count and split-adjusted cost basis. Recording a lot from one of those statements is not the mistake above -- you are stating a holding as it stands today, not reaching back to rewrite a lot that already lived through the split in your file.

The only thing zfin needs to know is whether the split is already baked into the number you typed. The cutover date answers that:

  • Shares entered as transacted (pre-split). The split happened after you recorded them, so set splits_current_through to a date before the split. zfin applies it.
  • Shares copied post-split from a statement. The number already includes the split, so set splits_current_through to the statement date (after the split). zfin leaves it alone instead of applying it twice.

Either way the rule holds: you never went back and edited an existing lot's shares. You either left it as transacted or recorded a fresh lot from a statement, and the cutover date tells zfin which.

Bright line: recording a lot from a post-split statement is fine. Reaching back to change an existing committed lot's shares because of a split is what corrupts your history. If you are tempted to edit a number that is already committed, set the cutover instead.

Verify

zfin audit's hygiene check lists any held symbol that has a post-purchase split but no splits_current_through yet, under Unhandled stock splits -- so you can see at a glance which rows still need the line:

zfin audit

Once set, zfin portfolio shows the effective (split-adjusted) share count and the correct market value, while your files stay untouched.

Next steps


Previous: Snapshots and history | Next: Plan for retirement | Documentation home