zfin/docs/reference/config/portfolio-srf.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

9.6 KiB

portfolio.srf reference

Your portfolio is a plain-text SRF file with one lot per line. A lot is a batch of shares of one security bought on one date in one account. Positions are aggregated from lots automatically.

zfin looks for portfolio.srf in ZFIN_HOME (or the current directory). You can split holdings across several portfolio_*.srf files; zfin union-merges every match (see Build your portfolio for the multi-file workflow).

File format

Every SRF line is a comma-separated list of key::value pairs. Typed values use a type tag:

  • key::value -- string
  • key:num:value -- number
  • key:bool:value -- boolean (true / false)

The first line must be the version header #!srfv1. Lines beginning with # are comments.

#!srfv1
# A stock/ETF lot
symbol::VTI,shares:num:100,open_date::2024-01-15,open_price:num:220.50,account::Brokerage

Lot fields

Field Type Required Description
symbol string Yes* Ticker or CUSIP. *Optional for cash lots.
shares number Yes Share count (or face value for cash/CDs). Negative for short option positions.
open_date string Yes** Purchase date YYYY-MM-DD. **Not required for cash/watch.
open_price number Yes** Purchase price per share. **Not required for cash/watch.
close_date string No Sale date. Omit for an open lot.
close_price number No Sale price per share.
security_type string No stock (default), option, cd, cash, illiquid, watch.
account string No Account name. Should match an account:: entry in accounts.srf.
note string No Free-text note (shown in cash/CD/illiquid tables).
ticker string No Ticker alias used for price fetching when symbol is a CUSIP.
price number No Manual price override (for securities the providers don't cover).
price_date string No Date of the manual price (YYYY-MM-DD), for staleness display.
drip bool No true if the lot is from dividend reinvestment (summarized as ST/LT groups).
maturity_date string No CD maturity or option expiry date (YYYY-MM-DD).
rate number No CD interest rate (e.g. 5.25 = 5.25%).

Security types

Type Meaning
stock (default) Stocks, ETFs, mutual funds. Priced from Tiingo (Yahoo fallback), aggregated by symbol.
option Option contracts. Shown separately; shares is the contract count (negative = written/short).
cd Certificates of deposit. Shown by maturity with rate and face value.
cash Cash, money-market, settlement balances. Grouped by account.
illiquid Real estate, vehicles, etc. Excluded from the liquid total; included in Net Worth.
watch Watchlist item: tracks price only, no position. See watchlist.srf.

Examples

Each line below is valid on its own. These mirror the bundled examples/ portfolios.

#!srfv1
# Stocks / ETFs (multiple lots of the same symbol aggregate)
symbol::VTI,shares:num:1100,open_date::2018-06-15,open_price:num:140.00,account::Pat 401k
symbol::VTI,shares:num:240,open_date::2015-01-08,open_price:num:103.40,account::Pat Roth

# Cash (no symbol, no open_date/open_price needed)
security_type::cash,shares:num:48000.00,open_date::2026-04-30,open_price:num:1.00,account::Joint taxable

# Closed (sold) lot
symbol::AMZN,shares:num:10,open_date::2022-03-15,open_price:num:150.25,close_date::2024-01-15,close_price:num:185.50

# DRIP lot (dividend reinvestment; summarized as ST/LT groups)
symbol::VTI,shares:num:0.234,open_date::2024-06-15,open_price:num:267.50,drip:bool:true,account::Brokerage

# CUSIP with a ticker alias for pricing (e.g. a 401k CIT share class)
symbol::02315N600,shares:num:1200,open_date::2022-01-01,open_price:num:140.00,ticker::VTTHX,account::Fidelity 401k,note::VANGUARD TARGET 2035

# Manual price override (security with no provider coverage)
symbol::NON40OR52,shares:num:500,open_date::2023-01-01,open_price:num:155.00,price:num:163.636,price_date::2026-02-27,account::Fidelity 401k

# Option: a written (short) call. Negative shares = contracts sold.
security_type::option,symbol::AAPL 06/20/2025 200.00 C,shares:num:-2,open_date::2025-01-15,open_price:num:12.50,option_type::call,underlying::AAPL,strike:num:200,maturity_date::2025-06-20,account::Brokerage

# CD
security_type::cd,symbol::912797KR0,shares:num:10000,open_date::2024-06-01,open_price:num:10000,maturity_date::2025-06-01,rate:num:5.25,account::Brokerage,note::6-Month T-Bill

# Illiquid asset (net worth only)
security_type::illiquid,symbol::HOME,shares:num:450000,open_date::2020-06-01,open_price:num:350000,note::Primary residence

Price resolution

For stock lots, the displayed price is resolved in this order:

  1. Live API -- latest close from cached candles (Tiingo, Yahoo fallback).
  2. Manual price -- the lot's price field, for securities without coverage.
  3. Average cost -- the position's open_price, as a last resort.

Manual-priced rows render in warning color so you know they may be stale; price_date tracks when you last updated them.

Ticker aliases and CUSIPs

Some securities (notably 401k CIT share classes) are identified by CUSIP but have a retail-ticker equivalent for pricing. Use ticker:: so symbol:: stays the display identifier while the alias drives API calls:

symbol::02315N600,ticker::VTTHX,...

If the CUSIP and retail ticker have different NAVs (common for CIT vs. retail funds), use a manual price:: instead of ticker::. To resolve a CUSIP to a ticker, use zfin lookup.

Advanced and option fields

These are less common but parse on any lot:

Field Type Applies to Description
price_ratio number share classes Multiplier applied to the fetched (alias) price to get the institutional NAV. E.g. if the retail class is $27.78 and the institutional class trades at $144.04, set price_ratio:num:5.185.
underlying string options Underlying stock symbol (e.g. AMZN).
strike number options Strike price.
maturity_date string options Expiration date (YYYY-MM-DD).
multiplier number options Shares per contract (default 100).
option_type string options call (default) or put.

An option lot is defined by these explicit fields, not by decoding the symbol -- symbol is just a display label, so set it to whatever your brokerage shows (e.g. AAPL 06/20/2025 200.00 C). For a worked two-lot example, see the covered call in Build your portfolio.

DRIP lots

Lots marked drip:bool:true are collapsed into short-term (ST) and long-term (LT) groups in the position detail view (split on the 1-year capital-gains threshold) rather than listed individually.

See also


Documentation home