| .forgejo/workflows | ||
| build | ||
| docker | ||
| docs | ||
| examples | ||
| src | ||
| .gitignore | ||
| .mise.toml | ||
| .pre-commit-config.yaml | ||
| AGENTS.md | ||
| build.zig | ||
| build.zig.zon | ||
| LICENSE | ||
| metadata.srf | ||
| README.md | ||
| TODO.md | ||
zfin
A financial data library, CLI, and terminal UI written in Zig. Tracks portfolios, analyzes trailing returns, displays options chains and earnings history, and renders price and projection charts (inline Kitty graphics or PNG export) -- all from the terminal.
Quick start
macOS (Apple Silicon) -- pre-built binary
# Download the latest aarch64-macos binary, make it executable, drop on $PATH
curl -L -o zfin \
https://git.lerch.org/api/packages/lobo/generic/zfin-aarch64-macos/latest/zfin-aarch64-macos
chmod +x zfin
sudo mv zfin /usr/local/bin/ # or anywhere on $PATH
Linux / building from source
# Requires Zig 0.16.0. With mise installed, `mise install` will pick the right
# version from .mise.toml. Otherwise grab Zig 0.16.0 manually.
zig build
# Binary lands at zig-out/bin/zfin
Configure
# Set at least one API key plus your contact email (see "API keys" below).
# The email is required for ETF profiles + `enrich` because SEC EDGAR mandates
# a User-Agent contact.
export TIINGO_API_KEY=your_key
export ZFIN_USER_EMAIL=you@example.com
Use it
zfin perf VTI # trailing returns
zfin quote AAPL # real-time quote
zfin options AAPL # options chains
zfin earnings MSFT # earnings history
zfin portfolio # portfolio summary (reads portfolio.srf)
zfin analysis # portfolio analysis (reads portfolio.srf + metadata.srf)
# Interactive TUI
zfin i # auto-loads portfolio.srf from cwd
zfin i -p portfolio.srf -w watchlist.srf
zfin i -s AAPL # start with a symbol, no portfolio
Building from source requires Zig 0.16.0.
Documentation
Full user documentation lives in docs/ and is built
around the runnable example portfolios in examples/.
- New here? Getting started -- install, configure, and run your first commands.
- Learn the model: Core concepts.
- Do a task (the user manual):
- Look something up (reference):
- CLI commands
- Config files:
portfolio.srf,accounts.srf,metadata.srf,projections.srf, and more - The interactive TUI
- Data providers and API keys
- Understand the why (explanation): caching, data providers, returns, the projection model, FAQ.
Data providers
zfin aggregates data from multiple free-tier APIs, using each for what it does best, with aggressive caching to stay within free-tier limits.
| Data type | Provider | Auth | Free-tier limit | Cache TTL |
|---|---|---|---|---|
| Daily candles (OHLCV) | Tiingo | TIINGO_API_KEY |
1,000 req/day, 50 req/hour | 23h45m |
| Real-time quotes | Yahoo Finance | None required | Unofficial | Never cached |
| Quote fallback | TwelveData | TWELVEDATA_API_KEY |
8 req/min, 800/day | Never cached |
| Dividends | Polygon | POLYGON_API_KEY |
5 req/min | 14 days |
| Splits | Polygon | POLYGON_API_KEY |
5 req/min | 14 days |
| Options chains | CBOE | None required | ~30 req/min (self-imposed) | 1 hour |
| Earnings | FMP | FMP_API_KEY |
250 req/day | 30 days* |
| ETF profiles | SEC EDGAR | ZFIN_USER_EMAIL |
10 req/sec | ~90 days |
| Classification | Wikidata + EDGAR | ZFIN_USER_EMAIL |
No per-day quota | Long-lived |
Not all keys are required; without a given key, that data type is simply unavailable. For per-provider notes, signup links, the full caching/TTL model, and the complete environment-variable list, see Data providers and API keys, Caching and data freshness, and Environment variables.
Architecture
src/
main.zig CLI dispatch + TUI entry point
root.zig Library root, exports all public types
Config.zig Configuration from env vars / .env files
service.zig DataService: cache-check -> fetch -> cache -> return
Date.zig Date type (i32 days since epoch); arithmetic, formatting
Money.zig Money formatting ($1,234.56; whole/trim/signed variants)
format.zig Shared formatters, braille engine, ANSI helpers
padded.zig Generic width/alignment wrapper for any formattable type
PortfolioData.zig Per-portfolio parse, price fetch, summary, background workers
portfolio_loader.zig Loads + union-merges portfolio_*.srf for CLI and TUI
compare.zig Composes two points in time (snapshot vs live) for `compare`
history.zig Reads history/<date>-portfolio.srf snapshots; aggregation
market.zig Market calendar; close-anchored cache freshness
git.zig Thin git subprocess wrappers (diff/walk tracked portfolios)
atomic.zig Crash-safe atomic file writes (tmp + fsync + rename)
stderr.zig Best-effort (non-throwing) stderr hint/progress writers
term_graphics.zig Kitty graphics emission for plain (non-TUI) CLI charts
term_query.zig Terminal capability probing for inline CLI graphics
chart_export.zig Chart-to-PNG export (`--export-chart <path>`)
comptime_validator.zig Shared comptime contract-validation helpers
models/
candle.zig OHLCV price bars
quote.zig Real-time quote data
dividend.zig Dividend records with type classification
split.zig Stock splits
option.zig Option contracts and chains
earnings.zig Earnings events with surprise calculation
etf_profile.zig ETF profiles with holdings and sectors
portfolio.zig Lots, positions, and portfolio aggregation
classification.zig Classification metadata parser
snapshot.zig Snapshot record types (history/*-portfolio.srf wire format)
transaction_log.zig transaction_log.srf wire format (transfer:: records)
shiller_year.zig One year of Shiller returns (S&P / bond / CPI)
providers/
tiingo.zig Tiingo: daily candles (primary)
yahoo.zig Yahoo Finance: quotes (primary), candle fallback
twelvedata.zig TwelveData: quote fallback
polygon.zig Polygon: dividends, splits (with forward-looking entries)
fmp.zig FMP: earnings (actuals + estimates)
cboe.zig CBOE: options chains (no API key)
Edgar.zig SEC EDGAR: ETF profiles (NPORT-P), ticker map, XBRL facts
Wikidata.zig Wikidata SPARQL: classification metadata for `enrich`
openfigi.zig OpenFIGI: CUSIP to ticker lookup
json_utils.zig Shared JSON parsing helpers
xml.zig Vendored XML parser used by Edgar.zig (see "Vendored code")
analytics/
indicators.zig SMA, Bollinger Bands, RSI
performance.zig Trailing returns (as-of-date + month-end)
risk.zig Per-symbol volatility, Sharpe, drawdown
portfolio_risk.zig Correlation-aware portfolio-level risk
valuation.zig Portfolio summary, allocations, covered-call adjustments
analysis.zig Breakdowns by class/sector/geo/account/tax
benchmark.zig Per-position benchmark returns
exposure.zig Look-through exposure (direct + ETF holdings)
milestones.zig Retirement-attainment grid
projections.zig Monte Carlo + percentile bands
forecast_evaluation.zig Forecast-vs-actual (convergence + return back-test)
observations.zig Portfolio sanity checks for the review surface
timeline.zig History-tab tier rollup
data/
ie_data.csv Raw Shiller S&P 500 + CPI dataset (CSV)
shiller.zig Shiller series, comptime-generated from ie_data.csv
imported_values.zig Back-history portfolio values (imported from spreadsheet)
staleness.zig Account-cadence staleness checks
Journal.zig acknowledgments.srf journal (acknowledged findings)
brokerage/
types.zig Normalized BrokeragePosition record + dollar parser
fidelity.zig Fidelity "Download Positions" CSV parser
schwab.zig Schwab export parser
wells_fargo.zig Wells Fargo export parser
views/
portfolio_sections.zig Portfolio view model (renderer-agnostic StyleIntent)
compare.zig Compare view model
history.zig History view model
projections.zig Projections view model
review.zig Per-holding performance + risk dashboard view model
observations_view.zig Findings list (engine output + acknowledgments)
cache/
store.zig SRF file cache with TTL freshness checks
net/
http.zig HTTP client with retries and server-error retry
RateLimiter.zig Token-bucket rate limiter
commands/
common.zig Shared CLI helpers (progress, formatting, color)
framework.zig CLI command framework + portfolio pattern resolution
TimeRange.zig Shared --range / time-window parsing
audit/ Brokerage reconciliation helpers for `audit`
(one .zig per CLI command: perf, quote, portfolio, analysis, history, ...)
charts/
chart.zig Price + Bollinger + volume + RSI renderer (z2d)
projection_chart.zig Percentile-band + median projection renderer
forecast_chart.zig Convergence / back-test line renderer
line_chart.zig Single-series value-over-time line renderer
axis.zig Shared axis-label helpers
draw.zig Shared z2d drawing primitives
text.zig 5x7 bitmap font for axis labels
tui.zig Interactive TUI App orchestrator
tui/
tab_framework.zig Tab contract + comptime validator
keybinds.zig Configurable keybinding system
theme.zig Configurable color theme
input_buffer.zig Modal text-input state machine
(nine *_tab.zig tabs: portfolio, analysis, review, projections,
history, quote, performance, earnings, options)
version.zig Build version string
Data files (user-managed, auto-detected in cwd or $ZFIN_HOME):
portfolio.srf Portfolio lots (positions and cost basis)
accounts.srf Account -> tax-type mapping for analysis
metadata.srf Per-symbol classification (class/sector/geo) for analysis
watchlist.srf Symbols to track in the TUI watchlist
transaction_log.srf Declared transfers so contributions math skips internal moves
projections.srf Retirement / Monte Carlo projection assumptions
theme.srf TUI color theme overrides
keys.srf TUI keybinding overrides
acknowledgments.srf Acknowledged review-tab findings (app-maintained)
Dependencies
| Dependency | Source | Purpose |
|---|---|---|
| SRF | Git | Cache file format and portfolio/watchlist parsing |
| libvaxis | Git (v0.6.0) | Terminal UI rendering |
| z2d | Git (v0.11.0) | Pixel chart rendering (Kitty graphics protocol) |
| zeit | Git (v0.9.0) | Calendar arithmetic + timezone conversion (ET) |
Building
Requires Zig 0.16.0. The canonical version is pinned in .mise.toml:
# Recommended: use mise to install the right Zig and ZLS automatically
mise install
# Or, with Zig 0.16.0 already on PATH:
zig build # build the zfin binary
zig build test # run all tests
zig build run -- <args> # build and run
The compiled binary is at zig-out/bin/zfin.
Vendored code
A small amount of third-party source is vendored directly into the
tree (rather than added as a Zig package dependency) where the
upstream is small, stable, and not packaged for build.zig.zon:
| File | Source | Purpose |
|---|---|---|
src/providers/xml.zig |
Snektron/vulkan-zig, via aws-zig | XML DOM parser used by the EDGAR provider for NPORT-P primary documents. |
Each vendored file carries a // VENDORED - see README.md header
identifying its upstream source. When updating, copy the new
upstream verbatim and re-add the header.
License
MIT