8.6 KiB
A periodic review
Goal: on a regular cadence -- weekly works well -- make zfin agree with your brokerages, see what changed since last time, and commit the reconciled state so the next review has a clean baseline to compare against.
This is the loop that ties the other guides together. Each step has its own guide for the details; this one is the routine you actually run. It settles into well under an hour once it's habit.
reconcile ──► what changed? ──► (projections) ──► commit
(audit) (compare) (optional) (baseline)
▲ │
└──────────────── next review ◄────────────────────────┘
1. Reconcile against your brokerages
Pull a fresh export from each brokerage that offers one and drop it in
your audit/ folder (or wherever you've pointed $ZFIN_AUDIT_FILES --
see Audit against your brokerage for the
export steps and auto-discovery rules). Then:
zfin audit
Fix any flagged share or cash discrepancy in portfolio.srf and re-run
until it reconciles. A tight edit loop helps: if you have
watchexec installed,
watchexec -- zfin audit
re-runs the audit on every save, so the remaining-discrepancy list shrinks live as you fix lots -- much faster than alt-tab, rerun, read, alt-tab, edit.
Reconcile first, for a reason. Step 2 splits your change in value into contributions vs. market gains, and that split is only as honest as your share counts. Reconcile before you read the headline or the attribution will lie to you.
Blind spots the audit can't see
- Accounts with no export (some insurers, some 401(k)
recordkeepers) -- check the latest statement and update
portfolio.srfby hand. - Payroll-driven cash -- e.g. ESPP contributions that haven't purchased yet won't appear in a positions export until the purchase posts. Reconcile those against a paystub.
- A small, expected standing discrepancy -- some accounts just sit a few dollars off every week. Note it and move on rather than chasing it each time.
- Lagging transaction views. A brokerage's positions view can update overnight before its transaction view posts the dividend, interest, or option assignment that caused the change. Trust the positions numbers and reconcile the totals; the cause-side record catches up later and isn't needed to get today's counts right.
2. The headline -- what changed since last time
One command gives you the whole "since last review" picture:
zfin compare 1W --projections
1Wis the point of comparison -- the snapshot from one week ago. Any relative shortcut or an explicitYYYY-MM-DDworks.--projectionsfolds in projected-return and safe-withdrawal (SWR@99%) deltas, then vs. now. (Costs ~1-2s per endpoint for the Monte Carlo search; add--no-eventsto exclude life events.)- No
--commit-beforeneeded. Attribution now anchors itself on the commit that recorded the comparison snapshot. See Attribution and commit timing.
Read off the liquid-total delta, the contributions-vs-gains split, the
per-symbol winners and losers, and the projection deltas. For most
weeks, this single command is the review. See
Snapshots and history for a full walk
through compare output.
3. (Optional) Full projections
compare --projections gives you the deltas but not the full benchmark
table or every scenario row. When you want the complete picture:
zfin projections # default: with life events (SS, college, ...)
zfin projections --no-events # baseline: life events excluded
zfin projections --as-of 1W # the same table as of last review
zfin projections --vs 1W # both ends of the window in one run
See Plan for retirement for what these rows mean.
4. Commit -- the baseline for next time
git add portfolio.srf metadata.srf history/
git commit -m "review 2026-06-20"
Committing does double duty:
- It snapshots the reconciled
portfolio.srf, and the day's snapshot file inhistory/rides along, so future--as-ofruns can read it. - zfin walks the git history of
portfolio.srffor contributions analysis, treating each commit as a reconciliation point. Commit timing sets next review's baseline -- a same-day commit keeps a weekly cadence clean.
If you keep your portfolio directory in git (recommended), this is also your backup and your audit trail.
Attribution and commit timing
compare and contributions work out "contributions vs. gains" by
walking the git history of portfolio.srf. The positional date (1W)
picks the snapshot whose prices you compare against; the attribution
needs a commit to anchor on. If those two describe different periods,
the split is wrong -- and it is wrong silently, because gains are derived
as a residual (delta - contributions), so anything the attribution
window misses is reported as market performance.
This is now handled for you. The default anchor is the commit that
ADDED history/<date>-portfolio.srf for the snapshot being compared
against. Because a review commits its snapshots together with the
reconciled portfolio.srf, that commit is that week's
reconciliation -- no naming convention, no dependence on how many times
you committed, and unaffected by later edits to the file.
Do not pass --commit-before HEAD. It used to be the recommended
workaround for reconciling on Saturday and committing on Monday, and it
worked only while HEAD happened to be the previous week's
reconciliation commit. Commit anything during a review -- a data fix, a
metadata tweak -- and the attribution window collapses to "uncommitted
edits only" while the value window still spans the week. One observed
case reported ~$23k of vested shares as market gains.
The flag remains for pinning a window deliberately; it is no longer a habit worth having.
Re-snapshot after reconciling
One ordering detail matters more than it looks. If a scheduled job
writes daily snapshots, the one for the last market day is captured
before you reconcile -- so it holds pre-reconcile share counts while
portfolio.srf ends up holding post-reconcile ones. The two then
disagree at the same commit, permanently, by one reconciliation:
history/2026-08-21-portfolio.srf AMZN 1569 (pre-reconcile)
portfolio.srf @ that same commit AMZN 1656 (post-reconcile)
compare reads the snapshot for "then" and the live portfolio for
"now", so those 87 shares appear in the value delta. Attribution walks
portfolio.srf across commits, where they were already present -- so no
contribution is found and the residual absorbs them.
Fix: run zfin snapshot --force after reconciling and before
committing. On a non-trading day it rewrites the last trading day's
file, so it never invents a weekend snapshot.
Relative dates
Every date-accepting command takes the same shorthand, so you rarely type a full date:
| Shortcut | Means |
|---|---|
1W |
one week ago |
3W |
three weeks ago |
1M |
one month ago |
1Q |
one quarter ago |
1Y |
one year ago |
compare, contributions --since/--until, projections --as-of/--vs,
snapshot --as-of, and history --since/--until all accept it (plus an
explicit YYYY-MM-DD).
If there's no snapshot yet
The comparison needs a snapshot to compare against. If your daily snapshot didn't run, or you're reviewing off your normal cadence, make one first:
zfin portfolio --refresh # fresh close prices for tracked symbols
zfin snapshot # writes history/<today>-portfolio.srf
See Snapshots and history for the cron setup that automates daily snapshots.
Next steps
- Audit against your brokerage -- step 1 in depth.
- Snapshots and history --
compare, snapshots, and the timeline. - Track contributions -- the contributions / gains attribution.
- Plan for retirement -- the projection rows.
compareandprojectionsreference -- every flag.
Previous: Audit against your brokerage | Next: Customize the TUI | Documentation home