zfin/docs/explanation/returns-and-performance.md
Emil Lerch 74fc219afd
All checks were successful
Generic zig build / build (push) Successful in 5m48s
Generic zig build / publish-macos (push) Successful in 11s
Generic zig build / deploy (push) Successful in 23s
add docs/guides
2026-06-22 14:53:53 -07:00

5.2 KiB

Returns and performance

zfin reports returns in a few different forms. This page explains what each means so you can read perf, review, and the portfolio summary correctly.

Price-only vs. total return

Two columns show up throughout zfin:

  • Price only -- the change in share price alone.
  • Total return -- price change plus reinvested dividends. This is the number that reflects what you actually earned.
                           Price Only   Total Return
  1-Year Return:              27.29%        28.79%
  3-Year Return:              20.75%        22.22%  ann.

Total return needs dividend history, which comes from Polygon -- so it requires POLYGON_API_KEY. Without it, you still get price-only returns. For a dividend payer like SCHD the gap between the two columns is large; for a non-payer it's near zero.

Annualized (CAGR)

Multi-year returns are annualized -- expressed as a compound annual growth rate (the constant yearly rate that produces the same total growth), marked ann. in the output. A "3-Year Return: 22.22% ann." means the holding grew as if by 22.22% every year for three years, not 22.22% total. One-year figures are already annual, so they carry no ann. tag.

As-of vs. month-end

perf prints two tables:

  • As-of -- returns through the latest available close. The most current view.
  • Month-end -- returns through the most recent calendar month-end. This matches how mutual funds and Morningstar quote their trailing numbers, so it's the apples-to-apples figure for comparison.
As-of 2026-06-04:        ...
Month-end (2026-05-31):  ...

Risk metrics

perf and review also report risk, computed from periodic returns:

  • Volatility -- how much returns vary; higher means a bumpier ride.
  • Sharpe ratio -- return earned per unit of volatility (risk- adjusted return). Higher is better; a negative Sharpe means the asset underperformed cash on a risk-adjusted basis over the window.
  • Max drawdown -- the largest peak-to-trough decline over the period; a worst-case-loss gut check.
  3Y-Vol  10Y-Vol    3Y-SR   10Y-SR   5Y-MaxDD
   13.4%    15.8%     1.29     0.90      24.8%

Reading the Sharpe ratio

Sharpe divides an asset's excess return -- its annualized return minus the risk-free rate -- by its volatility. It answers "how much extra return did I earn for the bumpiness I took on, versus just parking the money risk-free?"

zfin uses the average 3-month US T-bill rate (FRED series DTB3) over the same window as the return. That rate table is hand-maintained: it's refreshed about once a year, and when it falls behind zfin prints a T-bill risk-free rate table is overdue for refresh warning on stderr (also shown by zfin doctor). It blocks nothing -- it's a nudge. Because the same rate feeds every holding, a slightly stale rate barely shifts comparisons between them; refresh it when the nag appears.

Rough guide to what's "good":

Sharpe Read
> 1.0 Standout -- strong reward for the risk taken
0.5-1.0 Healthy -- where most solid long-term holdings sit
0-0.5 Mediocre -- barely beating cash once risk is accounted for
< 0 Lost to cash on a risk-adjusted basis (the negative case noted above)

These are the bands zfin uses to color the review dashboard: green above 0.5, yellow from 0 to 0.5, red below 0.

Prefer the longer window. A Sharpe measured over a single year is noisy -- one strong or weak quarter swings it. The 10-year figure averages across market regimes and is the steadier read, which is why zfin reports Sharpe at 3Y and 10Y rather than 1Y. When the two disagree, lean on the longer window to judge a holding's risk-adjusted quality.

Portfolio-level returns

The portfolio summary's Historical line and the projections benchmark block show your holdings' blended return -- each position's return weighted by its market value. It's a quick "how have my holdings done together" read, and it includes the effect of your allocation (a bond-heavy portfolio will trail an all-equity one, as you'd expect).

These returns describe the securities, not your personal money- weighted return. They don't account for the timing of your contributions. To separate new money from market movement over a period, use contributions.

The market value that weights these returns reflects zfin's covered-call cap: an in-the-money written call holds its covered shares at the strike rather than the live price, so it doesn't price the option contract itself. See covered-call valuation.

See also