finance library and cli/tui
Find a file
2026-06-27 10:20:48 -07:00
.forgejo/workflows initial attempt - macos build/publish 2026-05-30 10:49:49 -07:00
build full emdash (and other) scrub 2026-06-25 10:13:25 -07:00
docker create docker image 2026-04-18 12:41:22 -07:00
docs implementation of spending smile 2026-06-26 14:54:59 -07:00
examples implementation of spending smile 2026-06-26 14:54:59 -07:00
src move braille charts to its own space 2026-06-27 10:20:48 -07:00
.gitignore add fund expense ratio for projections/description of deltas vis-a-vis FireCALC 2026-06-24 09:02:57 -07:00
.mise.toml update mise config for use on risc-v 2026-06-19 09:45:36 -07:00
.pre-commit-config.yaml add in-kind transfer capability 2026-06-25 13:49:29 -07:00
AGENTS.md full emdash (and other) scrub 2026-06-25 10:13:25 -07:00
build.zig market-aware ttl 2026-06-25 14:46:08 -07:00
build.zig.zon market-aware ttl 2026-06-25 14:46:08 -07:00
LICENSE ai generated, functional, no code review yet 2026-02-25 14:10:27 -08:00
metadata.srf add international etfs to metadata 2026-04-09 17:04:00 -07:00
README.md update README 2026-06-26 17:01:37 -07:00
TODO.md move braille charts to its own space 2026-06-27 10:20:48 -07:00

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/.

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