zfin/docs/guides/classify-holdings.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

4.1 KiB

Classify your holdings

Goal: create a metadata.srf that tells zfin the asset class, sector, and geography of each symbol, so zfin analysis and zfin review -- and their TUI tabs -- can group your holdings by category and sector.

You'll need: a portfolio.srf (build one first). For the automatic path, set ZFIN_USER_EMAIL (SEC EDGAR requires a contact address). Full field list: metadata.srf reference.

Why classify?

Without metadata.srf, zfin can value your portfolio but can't group it. Classification feeds the Asset Category / Sector / Geographic breakdowns in zfin analysis, and the per-holding Sector column and grouping in zfin review -- each as a CLI command and as its tab in the TUI. A few lines unlock all of them:

  Asset Category
  Equity                   ██████████████████████████▋     89.2%  $1,233,151.30
  Fixed Income             ██                               7.0%  $96,922.00
  Cash                     █▏                               3.8%  $53,064.51

zfin enrich queries Wikidata and SEC EDGAR to generate classification lines for you. Point it at your portfolio and redirect to metadata.srf:

ZFIN_HOME=~/finance zfin enrich portfolio.srf > ~/finance/metadata.srf

It writes a complete SRF file (header included) with one entry per stock symbol. Symbols Wikidata doesn't know fall back to EDGAR's mutual-fund map; anything that misses both is emitted as a TODO line for you to fill in by hand.

To add a single symbol to an existing file, give enrich a symbol instead of a file -- it prints just the classification lines (no header), so you can append:

zfin enrich SCHD >> ~/finance/metadata.srf

Option B: write it by hand

For a small portfolio, hand-writing is quick. One line per symbol:

#!srfv1
symbol::VTI,sector::Diversified,geo::US,asset_class::US Large Cap
symbol::AGG,sector::Bonds,geo::US,asset_class::Bonds
symbol::QQQ,sector::Technology,geo::US,asset_class::US Large Cap

The symbol:: must match the symbol:: (or ticker::) used in your portfolio. Cash and CDs are classified as "Cash & CDs" automatically.

Blended funds

A target-date or balanced fund spans several asset classes. Add one line per slice with pct:num: weights that sum to ~100:

symbol::02315N600,asset_class::US Large Cap,pct:num:55
symbol::02315N600,asset_class::International Developed,pct:num:20
symbol::02315N600,asset_class::Bonds,pct:num:15
symbol::02315N600,asset_class::Emerging Markets,pct:num:10

Fixing uninformative sectors

ETF holdings data sometimes tags everything as the generic "Equity / Corporate," which collapses distinct holdings into one meaningless group. When that happens, set a bucket:: label yourself to a grouping that actually distinguishes them -- it overrides the auto-derived sector for concentration and dominance analysis. See the bucket field.

For example, two broad funds that would both auto-bucket as "Diversified" can be split into meaningful groups so concentration and dominance analysis treat them separately:

#!srfv1
symbol::VTI,sector::Diversified,geo::US,asset_class::US Large Cap,bucket::US Total Market
symbol::SCHD,sector::Diversified,geo::US,asset_class::US Large Cap,bucket::US Dividend

Verify

ZFIN_HOME=~/finance zfin analysis

If a symbol shows up under "Unclassified," it's missing a metadata entry (or the symbol doesn't match). Add a line and re-run.

Next steps


Previous: Build your portfolio | Next: Map your accounts | Documentation home