zfin/docs/reference/cli/contributions.md

6.1 KiB

zfin contributions

Show contributions, withdrawals, and lot-level changes between two points in your portfolio's git history.

Usage: zfin contributions [opts]

contributions diffs two git revisions of your portfolio*.srf files and attributes the share/lot changes to new money vs. market movement. Every file matching the glob is read at both revisions and merged, so a sold lot archived into a sibling portfolio_closed.srf is still seen. Your portfolio must be under git with commits over time.

Modes

Invocation Window
(no flags), dirty tree HEAD vs. working copy
(no flags), clean tree HEAD~1 vs. HEAD (review the last commit)
--since <DATE> commit at/before DATE vs. HEAD (or working copy if dirty)
--since <D1> --until <D2> commit at/before D1 vs. commit at/before D2

--until alone is rejected (the window is ambiguous). Dates accept YYYY-MM-DD or 1W/1M/1Q/1Y.

Options

Flag Effect
--since <DATE> Earliest side (resolves to commit at/before).
--until <DATE> Latest side (pair with --since).
--commit-before <SPEC> Pin the before commit directly (same grammar as --commit-after, minus working).
--commit-after <SPEC> Pin the after commit: YYYY-MM-DD, relative, HEAD, HEAD~N, SHA, or working.

Pass at most one of --since/--commit-before (same axis), and at most one of --until/--commit-after.

Example

zfin contributions --since 1Y

Internal movement

Money that was already inside an account is not a contribution -- it just changed form. Two shapes are detected automatically, with no bookkeeping on your part, and both report under Internal purchases rather than counting toward the total:

  • Buying with cash already in the account. The buy appears alongside the account's cash going down.
  • Reallocating -- selling one holding to buy another in the same account. The sale's proceeds offset the repurchase.
  • A CD paying out. A CD's payout counts like a sale: rolling it into a new CD, or buying with the payout, isn't new money.

A sale is valued at close_price when you record one (see portfolio.srf), which is what the sale actually realized. If you delete the lot outright instead, or record the close without a close_price, the current market price stands in -- accurate for a recent sale, less so for one made long before the end of the window. The report labels which was used: at close only when every lot on the line had a close_price, otherwise at mark.

An option removed after it expired is worth $0 -- whether it expired worthless or was exercised, the premium isn't proceeds. (An exercise shows up separately, as the stock and cash that moved.)

A partial sale is recorded by splitting the lot: keep the shares you still hold on one line, and put the sold shares on another with close_date and close_price. Only the sold shares count, at their close_price, whichever order the lines are in and whether the sold one stays in portfolio.srf or moves to portfolio_closed.srf. Deleting a lot you had already closed changes nothing, and neither does adding old closed lots to the file -- though an added one appears in the report as a purchase funded by its own sale.

Closing a position that has accumulated a lot per dividend reinvestment retires many lots at once, so sales collapse to one line per account and symbol, carrying the lot count and the total.

Proceeds still sitting in cash at the end of the window cannot have funded anything, and are treated accordingly. On an account marked cash_is_contribution::true they also cancel that account's cash credit, since the sale is not new money even though cash arrived.

A CD that matures pays out in the window containing its maturity date -- between the dates of the two commits being compared, or today for uncommitted changes -- whether you close it, delete it, archive it to portfolio_closed.srf, or leave it where it is. Closing or removing it in a later window changes nothing. A CD redeemed before maturity pays out in the window you close or remove it.

On an account marked cash_is_contribution::true, update its cash balance in that same window: a payout that reaches the cash line a commit later reads as new money there.

The same account can also hold something that pays - an HSA invested in a fund, say - and then payroll and dividends arrive in the same cash line. A declared cash dividend is carved out of the cash credit and shown under Cash deltas instead, with the payer, shares, rate and pay date beside it, exactly where the same dividend lands on an account without the flag. It counts when its pay date falls between the dates of the two commits being compared, on the shares held at the earlier one, and never for more than the cash that actually arrived. A payer with a dividend-reinvestment lot in the window is left alone, since its dividend bought shares instead. The figures come from zfin's dividend data, so --refresh-data=never uses what is cached, and a symbol with no data leaves its dividend counted as a contribution (zfin logs a warning naming it).

Movement between accounts is a different matter -- zfin cannot tell it from a contribution, so declare it in transaction_log.srf. An explicit record always wins over the automatic netting above.

See also


CLI command reference