zfin/docs/guides/read-your-portfolio.md

8.6 KiB

Read your portfolio

Goal: make sense of what zfin shows you. This guide walks the five commands you'll reach for most -- portfolio, analysis, review, exposure, and perf -- and explains how to read each one.

Every example below runs against the bundled pre-retirement-both household, so you can follow along verbatim:

ZFIN_HOME=examples/pre-retirement-both zfin portfolio

(Dollar figures depend on live prices, so yours will differ.)

portfolio: positions and value

ZFIN_HOME=examples/pre-retirement-both zfin portfolio
Portfolio Summary (examples/pre-retirement-both/portfolio.srf)
========================================
  Value: $1,383,137.81  Cost: $658,837.01  Gain/Loss: +$724,300.80 (109.9%)
  Lots: 13 open, 0 closed    Positions: 5 symbols
  Historical:  1M: +3.2%  3M: +13.3%  1Y: +24.5%  3Y: +56.4%  5Y: +56.1%  10Y: +182.6%

  Symbol    Shares   Avg Cost      Price     Market Value      Gain/Loss   Weight ...
  VTI       2480.0    $138.35    $373.38      $925,982.40 +  $582,874.40    66.9%
    open      1100.0    $140.00                 $410,718.00 +  $256,718.00          2018-06-15 LT Pat 401k
    ...

How to read it:

  • The header line is your liquid total, total cost, and aggregate gain/loss.
  • Historical is the portfolio's blended price return over trailing windows -- a quick "how have my holdings done" gut check.
  • Each position shows aggregated shares, average cost, current price, market value, gain/loss, and weight. Indented open rows are the individual lots, tagged LT/ST (long/short-term holding period) and the account.
  • Cash is summarized by account at the bottom.

Manual-priced rows render in warning color so you know the price may be stale. Full output shape: zfin portfolio.

Covered calls

zfin values a written (short) call by capping the covered shares at the strike, rather than pricing the option contract -- it never looks up a live option quote. When the call is in-the-money (the stock trades above the strike), the covered shares are valued at the strike, since that's the price you'd be assigned at; an out-of-the-money call gets no adjustment, and an expired or closed call stops capping.

This differs from your brokerage, which tracks the call as its own line with its own mark-to-market gain/loss. Example: you hold 100 MSFT and wrote a $500 call. If MSFT trades at $510, zfin values those shares at $50,000 (100 x the $500 strike), not $51,000 (100 x $510) -- the $1,000 of upside above the strike now belongs to the call holder. A brokerage would instead show $51,000 of stock plus a separate, losing short-call position.

This is deliberate, and for a covered call used as an exit strategy it's the more useful number: the cap is the value you'll actually realize when the call assigns. Mark-to-market would understate it, because an in-the-money call also carries time value you'd only pay to buy it back -- which a let-it-assign writer never does. (At $510 the call might mark around $14, so a brokerage nets $496/share even though you'll realize the full $500 at assignment.) The flip side: zfin is built for covered-call and buy-write investors, not active option trading -- it deliberately doesn't track the live mark-to-market swings of contracts you intend to trade rather than hold to assignment.

analysis: allocation breakdowns

ZFIN_HOME=examples/pre-retirement-both zfin analysis

analysis answers "how is my money allocated?" along five axes: Asset Category, Sector, Geographic, By Account, and By Tax Type. Each bar is a share of your liquid total.

  By Tax Type
  Traditional (Pre-Tax)    █████████████████▋              58.9%  $815,290.06
  Taxable                  ██████▍                         21.6%  $299,010.60
  Roth (Post-Tax)          █████                           16.9%  $233,732.95
  HSA (Triple Tax-Free)    ▊                                2.5%  $35,104.20

The Sector and Asset Category axes need metadata.srf; the Tax Type axis needs accounts.srf. Anything missing classification lands under "Unclassified" / "Unknown."

Umbrella exposure

A personal umbrella insurance policy covers liability -- lawsuits and judgments -- above your auto/home limits, and you size it to the assets you'd need to protect. The last block estimates that target: how much of your liquid net worth is exposed to a civil judgment because it sits outside judgment-protected retirement accounts:

  Umbrella exposure
    Total liquid:                              $1,383,137.81
    Shielded (retirement accounts):            $1,084,127.21
    Exposed (taxable + non-shielded pre-tax):  $299,010.60 (21.6%)
                                               ↑ approximate umbrella target

The default rule treats anything that isn't taxable as shielded. Override per account with shielded:bool:false in accounts.srf -- IRA protection varies by state and isn't modeled automatically.

review: per-holding performance and risk

ZFIN_HOME=examples/pre-retirement-both zfin review

review is a dashboard: one row per holding with trailing returns, volatility, Sharpe ratio, max drawdown, and a taxable-percentage column, plus automated findings at the bottom.

  Symbol   Sector                   Wt%       1Y       3Y  ...   3Y-SR   10Y-SR   5Y-MaxDD    Tax%
  VTI      Diversified            66.9%   +30.2%   +23.3%  ...    1.29     0.90      24.8%   10.9%
  QQQ      Technology              3.5%   +43.1%   +29.3%  ...    1.41     1.16      32.6%    0.0%

  Findings (2 active, 0 acked, 0 resolved)
  ⚠️ VTI at 66.9% of liquid (warn at 50.0%, flag at 70.0%)
  ❌️ Diversified sector at 85.7% (warn at 60.0%, flag at 75.0%)

The findings flag concentration, sector dominance, volatility outliers, and tiny positions against configurable thresholds. The status icons at the top summarize which checks fired. See zfin review.

Acknowledging findings

Findings are re-derived every run, so one you've already judged acceptable would otherwise nag you forever. Acknowledge it to record why and drop it from the active list. This is an interactive step -- in the TUI Review tab, move the cursor to a finding and press:

  • a -- acknowledge it (you can type a short reason, saved as a note).
  • U -- un-acknowledge (return it to the active list).
  • v -- toggle whether already-acknowledged findings are shown.

(Those are the defaults; ? shows your current bindings.) Acknowledgments persist in acknowledgments.srf beside your portfolio, so they survive across runs and are versioned with the rest of your data. The CLI honors them -- zfin review --show-acked includes acked findings in the table -- but only the TUI can add or remove them.

exposure: look-through to a single symbol

ZFIN_HOME=examples/pre-retirement-both zfin exposure SPY

exposure answers "how much of X do I really own?" -- combining direct holdings with look-through into the ETFs you hold (matched by CUSIP against each ETF's latest reported holdings):

Exposure to SPY  (examples/pre-retirement-both/portfolio.srf)
========================================
  Total exposure    17.3%     $238,956.80
    Direct          17.3%     $238,956.80
    Look-through     0.0%           $0.00

This is most interesting for an individual stock you also hold inside broad-market ETFs (e.g. checking your true NVDA exposure across VTI, SPY, and QQQ). See zfin exposure.

perf: trailing returns for one symbol

ZFIN_HOME=examples/pre-retirement-both zfin perf VTI

perf shows Morningstar-style trailing returns (price-only and total return), as-of the latest close and as-of the most recent month-end, plus risk metrics. For what "total return," "annualized," and "month-end" mean, see Returns and performance.

Where to go next


Previous: Map your accounts | Next: Track contributions | Documentation home