# zfin

A financial data library, CLI, and terminal UI written in Zig. Tracks portfolios, analyzes trailing returns, displays options chains, earnings history, and more -- 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/
  root.zig              Library root, exports all public types
  format.zig            Shared formatters, braille engine, ANSI helpers
  config.zig            Configuration from env vars / .env files
  service.zig           DataService: cache-check -> fetch -> cache -> return
  models/
    candle.zig          OHLCV price bars
    date.zig            Date type with arithmetic, snapping, formatting
    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
    quote.zig           Real-time quote data
  providers/
    tiingo.zig          Tiingo: daily candles (primary), supplementary div/split merge
    twelvedata.zig      TwelveData: quote fallback
    polygon.zig         Polygon: dividends, splits (primary, 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), mutual-fund ticker map, XBRL company facts
    Wikidata.zig        Wikidata SPARQL: classification metadata for `enrich`
    yahoo.zig           Yahoo Finance: quotes (primary), candles (Tiingo fallback)
    openfigi.zig        OpenFIGI: CUSIP to ticker lookup
    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            Volatility, Sharpe, drawdown
    valuation.zig       Portfolio summary, allocations, covered call adjustments
    analysis.zig        Portfolio analysis engine (breakdowns by class/sector/geo/account/tax)
  cache/
    store.zig           SRF file cache with TTL freshness checks
    net/
      http.zig            HTTP client with retries and server error retry
      rate_limiter.zig    Token-bucket rate limiter
  commands/
    common.zig          Shared CLI helpers (progress, formatting)
    perf.zig            Trailing returns command
    quote.zig           Quote command
    ... (14 command files)
  tui.zig               Interactive TUI application
  tui/
    chart.zig           z2d pixel chart renderer (Kitty graphics)
    keybinds.zig        Configurable keybinding system
    theme.zig           Configurable color theme
```

Data files (user-managed, in project root):
```
portfolio.srf           Portfolio lots
metadata.srf            Classification metadata for analysis
accounts.srf            Account to tax type mapping for analysis
```

### 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)   |

## 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 -- <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](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
