# 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 ```bash # 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 ```bash # 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 ```bash # 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 ```bash 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/`](docs/README.md) and is built around the runnable example portfolios in [`examples/`](examples/). - **New here?** [Getting started](docs/getting-started.md) -- install, configure, and run your first commands. - **Learn the model:** [Core concepts](docs/explanation/concepts.md). - **Do a task (the user manual):** - [Build your portfolio](docs/guides/set-up-your-portfolio.md) - [Classify your holdings](docs/guides/classify-holdings.md) and [map your accounts](docs/guides/set-up-accounts.md) - [Read your portfolio](docs/guides/read-your-portfolio.md) - [Plan for retirement](docs/guides/plan-retirement.md) - [Snapshots and history](docs/guides/snapshots-and-history.md), [track contributions](docs/guides/track-contributions.md) - [Audit against your brokerage](docs/guides/audit-against-brokerage.md) - [Customize the TUI](docs/guides/customize-the-tui.md), [offline use and refreshing data](docs/guides/offline-and-refresh.md) - **Look something up (reference):** - [CLI commands](docs/reference/cli/index.md) - Config files: [`portfolio.srf`](docs/reference/config/portfolio-srf.md), [`accounts.srf`](docs/reference/config/accounts-srf.md), [`metadata.srf`](docs/reference/config/metadata-srf.md), [`projections.srf`](docs/reference/config/projections-srf.md), and [more](docs/reference/config/environment.md) - [The interactive TUI](docs/reference/tui.md) - [Data providers and API keys](docs/reference/providers.md) - **Understand the why (explanation):** [caching](docs/explanation/caching.md), [data providers](docs/explanation/data-providers.md), [returns](docs/explanation/returns-and-performance.md), [the projection model](docs/explanation/projections-model.md), [FAQ](docs/explanation/faq-troubleshooting.md). ## 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](docs/reference/providers.md), [Caching and data freshness](docs/explanation/caching.md), and [Environment variables](docs/reference/config/environment.md). ## 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/-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 `) 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](https://git.lerch.org/lobo/srf) | Git | Cache file format and portfolio/watchlist parsing | | [libvaxis](https://github.com/rockorager/libvaxis) | Git (v0.6.0) | Terminal UI rendering | | [z2d](https://github.com/vancluever/z2d) | Git (v0.11.0) | Pixel chart rendering (Kitty graphics protocol) | | [zeit](https://github.com/rockorager/zeit) | Git (v0.9.0) | Calendar arithmetic + timezone conversion (ET) | ## Building Requires Zig 0.16.0. The canonical version is pinned in `.mise.toml`: ```bash # 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 -- # 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](https://github.com/Snektron/vulkan-zig/blob/797ae8af88e84753af9640266de61a985b76b580/generator/xml.zig), via [aws-zig](https://github.com/elerch/aws-sdk-for-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